0

I want to set the contents of MTKView (or GLKView/CAEAGLLayer) to black as soon just as the application resigns active. What is the quickest and most reliable way to set it to it's clear color(say black) and display it?

Deepak Sharma
  • 5,577
  • 7
  • 55
  • 131
  • You can follow the same general approach as any app that needs to obscure sensitive information in the task switcher: listen for `applicationDidEnterBackground(_:)` and `applicationWillEnterForeground(_:)`. The system will capture the appearance of the UI as it is configured upon returning from the did-enter-background call. In your case, you might need to play with ensuring the cleared contents gets presented to the screen by your renderer, but you could also follow [this approach](https://developer.apple.com/library/archive/qa/qa1838/_index.html) to get it done cheaply. – warrenm Aug 19 '19 at 18:05
  • Oh no, I just want to blacken MTKView, not other subviews such as buttons. Is there a way to force clear MTKView? – Deepak Sharma Aug 19 '19 at 19:38
  • The clear color configured on an `MTKView` is simply set as the clear color of the first color attachment of the render pass descriptors it vends. So "blanking" it would just consist of setting the clear color (if necessary) and rendering to its current drawable with a dummy render command encoder, then presenting that drawable, ensuring that that presentation happens before the crucial moment when the app switcher screen grab takes place. That last part may or may not be tricky, which is why I hedged in my first comment. – warrenm Aug 19 '19 at 20:37
  • Note that you might also need to pause the `MTKView` beforehand to prevent it from trying to draw on its usual display link cadence, then unpause it when you enter the foreground again. – warrenm Aug 19 '19 at 20:39

1 Answers1

6

In order to blank a MTKView upon entering the background, you must render a blank frame before returning from the delegate callback to the applicationDidEnterBackground(_:) method on your UIApplicationDelegate object.

It is not sufficient to listen for the UIApplication.didEnterBackgroundNotification; snapshots are captured before notification observers are notified of the state change.

This implies that you should pass the message that your application has entered the background down from your app delegate to the relevant view controller(s), and force them to immediately render a blank frame, before returning from the delegate method (meaning no posting notifications, and no dispatching async to different threads). Here's a method that clears a MTKView to black and waits for the drawing and presentation to be scheduled before returning:

func drawBlankAndWait(_ mtkView: MTKView) {
    mtkView.clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
    let commandBuffer = commandQueue.makeCommandBuffer()!
    guard let renderPassDescriptor = mtkView.currentRenderPassDescriptor else { return }
    let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
    renderEncoder.endEncoding()
    let drawable = mtkView.currentDrawable!
    commandBuffer.present(drawable)
    commandBuffer.commit()
    commandBuffer.waitUntilScheduled()
}

Upon receiving an applicationWillEnterForeground(_:) call, you can restore any state you might have set when entering the background, including the view's paused state.

warrenm
  • 31,094
  • 6
  • 92
  • 116
  • When I do it, I get a crash and this error - "Execution of the command buffer was aborted due to an error during execution. Discarded (victim of GPU error/recovery) (IOAF code 5)" – Deepak Sharma Aug 20 '19 at 07:06
  • I do not care about snapshots, my video preview session disconnects sometimes and the stale video frame is visible on screen. I just want to show a blank screen in case user quits the app and reenters, or the session disconnects for any reason. – Deepak Sharma Aug 20 '19 at 07:09
  • An IOAF Error 5 is almost never the first error printed to the console; are there other errors further up in the log? I tested this on iOS 12.4 and iPadOS 13.0 beta 6 and it seemed to work. – warrenm Aug 20 '19 at 17:32
  • Hello @warrenm, sorry for late response, here is what I get before IOAF error - "[CAMetalLayerDrawable texture] should not be called after already presenting this drawable. Get a nextDrawable instead." – Deepak Sharma Aug 23 '19 at 06:25