swiftsprite-kitskeffectnode

FPS issues with adding a blur using SKEffectNode


If there a better way of creating a blur effect? It seems like the way I am currently doing it creates FPS issues especially on older phones. It seems like the higher the blurAmount the lower the FPS. Could the blendMode be the reason here?

        if effectsNode.parent == nil {
            let filter = CIFilter(name: "CIGaussianBlur")
            let blurAmount = 15.0
            filter!.setValue(blurAmount, forKey: kCIInputRadiusKey)

            effectsNode.filter = filter
            effectsNode.blendMode = .add

            sceneContent.removeFromParent()
            effectsNode.addChild(sceneContent)
            addChild(effectsNode)
        }

When I pause my game, I call blurScreen() which does the following code above. However, it seems like my fps drops over time the longer the game is paused. I tried taking blurScreen() out and the FPS issues went away. How is the FPS dropping over time when blurScreen() is only called once?

EDIT:

func pauseGame() {
    sceneContent.isPaused = true
    intermission = true

    physicsWorld.speed = 0

    blurScreen()
} 

Here is the code in touchesEnded()

// Tapped pause or pause menu options
if name == "pause" && touch.tapCount == 1 && pauseSprite.alpha == 1.0 && ((!sceneContent.isPaused && !GameData.shared.midNewDay) || (!sceneContent.isPaused && sceneElements[0].editingMode)) {
                    SKTAudio.sharedInstance.pauseBackgroundMusic()
                    SKTAudio.sharedInstance.playSoundEffect("Sounds/pause.wav")

                   pauseSprite.run(SKAction.sequence([SKAction.scale(to: 1.2, duration: 0.10), SKAction.scale(to: 1.0, duration: 0.10)])) { [unowned self] in
                        self.createPauseMenu()
                        self.pauseGame()
                    }

                    return
                }

Update method

override func update(_ currentTime: TimeInterval) {
    if GameData.shared.firstTimePlaying && GameData.shared.distanceMoved > 600 && !step1Complete {
        tutorial2()
    }

    // Check for game over
    if GameData.shared.hearts == 0 && !gameEnded {
        gameOver()
    }

    // If we're in intermission, do nothing
    if intermission || sceneContent.isPaused {
        return
    }

    // some more stuff unrelated to pausing
}

Solution

  • You are running an effect node on the entire scene, that scene is going to be rendering that effect every frame which is going to put a lot of work on your system. If you do not have any animations going on behind it, I would recommend converting your effect node to a sprite node by doing this

    var spriteScene : SKSpriteNode!
    func blurScreen() { 
        DispatchQueue.global(qos: .background).async { 
            [weak self] in
            guard let strongSelf = self else { return }
            let effectsNode = SKEffectNode() 
    
            let filter = CIFilter(name: "CIGaussianBlur") 
            let blurAmount = 10.0 
            filter!.setValue(blurAmount, forKey: kCIInputRadiusKey) 
    
            effectsNode.filter = filter 
            effectsNode.blendMode = .add 
    
            strongSelf.sceneContent.removeFromParent() 
            effectsNode.addChild(strongSelf.sceneContent) 
    
            let texture = self.view!.texture(from: effectsNode) 
            strongSelf.spriteScene = SKSpriteNode(texture: texture) 
            strongSelf.spriteScene.anchorPoint = CGPoint(x: 0.5, y: 0.5) 
    
            DispatchQueue.main.async { 
                strongSelf.sceneContent.removeFromParent() 
                strongSelf.addChild(strongSelf.spriteScene) 
            } 
        } 
    }