美文网首页iOS程序猿
记一个SceneKit Morpher引发的Cras

记一个SceneKit Morpher引发的Cras

作者: 皮皮Warrior | 来源:发表于2019-01-27 17:31 被阅读33次

    记一个SceneKit Morpher引发的Crash

    tags: AR&3D SceneKit

    背景

    Animoji外网遇到一些Crash, 量不算大但一直存在,由于测试很难复现,只能靠看Log归纳用户的操作路径,对可疑点进行排查。

    分析

    猜测

    外网Crash的堆栈顶都是C3DGeometryGetMesh,进入C3DGeometryGetMesh大多是C3DMorpherUpdateIfNeed或者C3DMorpher的其他方法,但是再往后看调用者,就特别随机了……有时是在更新贴图,有时是在更新表情,甚至是挂在渲染时钟里,没有任何业务代码。
    C3D开头的类多是SceneKit、SpriteKit或ModelI/O的底层实现, 从类名结合具体业务逻辑,可以断定和SCNMorpher有关。
    Animoji是使用SCNMorpher做表情动画的,当用户选择不同Animoji模型,我会根据优先级加载不同表情,从这里入手开始查起。

    Animoji

    定位原因

    首先我先取消了分批加载表情的策略,在创建模型时就完成加载完表情再去渲染,结果灰度用户还是Crash……
    之后分析了业务逻辑和Log, 觉得有可能SCNMohpher不是线程安全的,因为Morpher的创建是异步的。
    猜测苹果的实现是异步加载具体网格的顶点数据到自己的Targets的数组,在更新时使用Metal计算每个顶点的变形后位置。

    尝试复现

    基于上面的分析,首先写了一个时钟,30帧调用选择不同Animoji的接口,果然Crash了,栈的结构与外网的类似。


    Crash Stack 现场 现场2

    再次分析

    从Crash现场和寄存器的来看,Morpher要加载某一个网格时,这个网格的数据是空的,所以Crash了,佐证了我之前的判断。但是如果Morpher这么不安全,那么这个Bug早就应该大面积爆发了,而且Morpher的接口设计也没有加载完成的回调。
    在Radar bug给苹果后,继续分析这个Bug的成因,毕竟项目还是要上线的。
    分析业务代码,发现Animoji在更换模型时,是重复利用一个Node来操作,更换时只是替换这个Node的Geometry和Morpher等,再控制Node旋转移动等。
    由于Morpher是附在Node上的,猜测苹果的实现,Node上可能有Morpher的信息,再一次做实验,用时钟30帧一次更换模型,这一次更换时会重新创建Node,将旧的Node从渲染场景中剥离,并在更新表情时判断Morpher的Targets数量是不是合预期。

    // MARK: - code doesn't crash
    // Change Animoji
    [self.fakerFace removeFromParentNode];
    SCNNode *node = [SCNNode node];
    SCNMorpher *morpher = [SCNMorpher new];
    morpher.calculationMode = SCNMorpherCalculationModeNormalized;
    morpher.targets = arr;
    morpher.unifiesNormals = YES;
    return morpher;
    node.morpher = morpher;
    self.fakerFace = node;
    [self.headParentNode addChildNode:self.fakerFace];
    
    //MARK: - In Render Loop
    if (self.fakerFace.morpher.targets > ANIMOJI_BLENDS_COUNT){
        // Update Animoji
    }
    

    实验结果表明不会Crash了,就算以60调用也没有问题。

    解决

    修改代码后,外网已经没有这个Crash了,修复这个Bug主要靠猜苹果的实现……
    也给大家分享下这个坑,使用Morpher要注意它的异步创建特点,同时要更换Morpher时要换个Node去持有它,不要直接加载到正在渲染循环中的Node,同时要判断Targets是否符合预期。不然底层代码会在取网格数据是碰到空值,造成Crash.

    相关文章

      网友评论

        本文标题:记一个SceneKit Morpher引发的Cras

        本文链接:https://www.haomeiwen.com/subject/fgcnjqtx.html