SceneKit:在 SCNView 上渲染 SpriteKit 粒子系统时应用程序崩溃,当所有代码似乎都是系统代码的一部分时如何调试

Posted

技术标签:

【中文标题】SceneKit:在 SCNView 上渲染 SpriteKit 粒子系统时应用程序崩溃,当所有代码似乎都是系统代码的一部分时如何调试【英文标题】:SceneKit: app crashes when rendering SpriteKit particle system over SCNView, how to debug when all code seems to be part of system code 【发布时间】:2018-09-01 19:44:58 【问题描述】:

SCNViewoverlaySKScene 属性中运行SpriteKit 粒子系统会导致应用崩溃,并显示下面的堆栈跟踪。

根据堆栈跟踪,似乎所有系统代码都在运行,那么您应该如何调试崩溃,更重要的是,确定它是 SceneKit/SpriteKit 的错误还是应用程序中的错误?

不管怎样,下面是添加粒子系统的函数,但堆栈跟踪中没有引用它。

fileprivate func animateFireworks(scnPoint: CGPoint, color: UIColor) 
    // Set emitter properties
    let particleBirthRate = CGFloat(150)
    let maxParticles = 50
    let numEmitters = 5

    // SceneView point -> SpriteKit point
    let skOverlayPoint = skOverlay.convertPoint(fromView: scnPoint)

    // Create emitters
    for _ in 0..<numEmitters 
        let fireworksEmitter = SKEmitterNode(fileNamed: ExplosionEmitterFilename)!

        // Set particle size
        let particleSize = CGSize(width: 20, height: 20)
        fireworksEmitter.particleSize = particleSize

        // Set color for emitter
        fireworksEmitter.particleColorSequence = nil
        fireworksEmitter.particleColor = color

        // Set number of particles
        fireworksEmitter.particleBirthRate = particleBirthRate
        fireworksEmitter.numParticlesToEmit = maxParticles

        // Position at point
        fireworksEmitter.position = skOverlayPoint

        // Add to SpriteKit overlay
        skOverlay.addChild(fireworksEmitter)

        // Remove emitter, <animationDur> is only a rough estimate
        let animationDur = 3 * Double(fireworksEmitter.particleLifetime + fireworksEmitter.particleLifetimeRange)
        delay(animationDur) 
            fireworksEmitter.removeFromParent()
        
    



func delay(_ delay:Double, closure:@escaping ()->()) 
    DispatchQueue.main.asyncAfter(
        deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)



> Thread 11 Queue : com.apple.scenekit.renderingQueue.SCNView0x121d8d570
> (serial)
> #0    0x000000019c710aac in SKCParticleManager::enumerateParticleSystems(void (SKCParticleSystem*)
> block_pointer) ()
> #1    0x000000019c723d74 in SKCParticleSystemNode::generateRenderData_Quads(SKCRenderInfo*) ()
> #2    0x000000019c7236c0 in SKCParticleSystemNode::addRenderOps_Quads(SKCRenderInfo*,
> std::__1::shared_ptr<jet_command_buffer> const&) ()
> #3    0x000000019c71dc4c in SKCRenderer::expandRenderGroup(std::__1::shared_ptr<SKCRenderSortGroup>
> const&, std::__1::shared_ptr<jet_command_buffer> const&) ()
> #4    0x000000019c71b238 in SKCRenderer::expandRenderPass(std::__1::shared_ptr<SKCRenderPass>
> const&, std::__1::shared_ptr<jet_command_buffer> const&) ()
> #5    0x000000019c71a888 in SKCRenderer::render(SKCNode*, float __vector(4), std::__1::shared_ptr<jet_framebuffer> const&, unsigned int __vector(4), simd_float4x4, bool, NSDictionary*, SKCStats*,
> SKCStats*, double) ()
> #6    0x000000019c6516a4 in -[SKSCNRenderer renderWithEncoder:pass:commandQueue:] ()
> #7    0x0000000199479fc8 in -[SCNRenderContextMetal renderSKSceneWithRenderer:overlay:atTime:] ()
> #8    0x0000000199525018 in -[SCNRenderer _drawOverlaySceneAtTime:] ()
> #9    0x00000001995e199c in __C3DEngineContextRenderPassInstance ()
> #10   0x00000001995e27cc in C3DEngineContextRenderMainTechnique ()
> #11   0x0000000199526508 in -[SCNRenderer _renderSceneWithEngineContext:sceneTime:] ()
> #12   0x0000000199526688 in -[SCNRenderer _drawSceneWithNewRenderer:] ()
> #13   0x0000000199526ccc in -[SCNRenderer _drawScene:] ()
> #14   0x0000000199527144 in -[SCNRenderer _drawAtTime:] ()
> #15   0x00000001995cfa74 in -[SCNView _drawAtTime:] ()
> #16   0x00000001994876c0 in __69-[NSObject(SCN_DisplayLinkExtensions) SCN_setupDisplayLinkWithQueue:]_block_invoke ()
> #17   0x000000019959700c in __36-[SCNDisplayLink _callbackWithTime:]_block_invoke ()
> #18   0x00000001042a12cc in _dispatch_call_block_and_release ()
> #19   0x00000001042a128c in _dispatch_client_callout ()
> #20   0x00000001042aff80 in _dispatch_queue_serial_drain ()
> #21   0x00000001042a47ec in _dispatch_queue_invoke ()
> #22   0x00000001042b0f6c in _dispatch_root_queue_drain_deferred_wlh ()
> #23   0x00000001042b8020 in _dispatch_workloop_worker_thread ()
> #24   0x00000001858a6f1c in _pthread_wqthread ()

【问题讨论】:

我喜欢你的名字非常适合这篇文章。 【参考方案1】:

单独查看此函数时很难判断发生了什么,但崩溃堆栈跟踪行 SKCParticleManager::enumerateParticleSystems(void (SKCParticleSystem*) 让我怀疑该问题与粒子发射器的添加/删除有关,可能在队列之外主队列。

SKNode docs:

对节点的操作必须发生在主线程中。

我会仔细检查 addChildremoveFromParent 方法是否仅在主队列上被调用。

【讨论】:

有趣的想法!这是发射器被移除的唯一地方,你是说在delay 函数中代码可能被从主队列中调用吗? 我说不清楚。 delay 是如何实现的? animateFireworks 可以从主队列中调用吗? 刚刚添加了delay 的代码。 animateFireworks 仅在用户点击屏幕时调用。谢谢! 看起来延迟只会在主队列上调度。我会尝试分别评论 addChildremoveFromParent 以缩小导致问题的范围。

以上是关于SceneKit:在 SCNView 上渲染 SpriteKit 粒子系统时应用程序崩溃,当所有代码似乎都是系统代码的一部分时如何调试的主要内容,如果未能解决你的问题,请参考以下文章

ARMeshAnchor – SceneKit SCNView 渲染器 EXC_BAD_ACCESS

SCNView第一次渲染场景很慢

SceneKit开发教程02 SCNScene和SCNView

Scenekit:动画时模型改变大小

DefaultCameraController 上的 SceneKit 动画

iOS 12 中autoenablesDefaultLighting 太亮,SCNView.pointOfView 无效