Reasons for nil currentRenderPassDescriptor?

I released an iOS app recently, and I'm seeing some crashes where an MTKView's currentRenderPassDescriptor is nil. I never saw these on my devices, but I see some from users, reported in Xcode's Organizer window.


The docs for this property say: This property is nil if the view’s device is not set. Your app should check that both currentRenderPassDescriptor and currentDrawable are not nil before attempting to draw.


I will follow that and skip drawing if it's nil. But, I *am* setting the view's device, so I'm wondering: why is the value nil?


Rob

Accepted Reply

TL;DR: nil means the GPU is working on too many frames at once so you need skip the frame


It can be nil if CoreAnimation can't provide a drawable which the Metal Kit view needs to set in the currentRenderPassDescriptor. Core Animation may not be able to provide a drawable because drawables are limited and the GPU needs them all. The GPU may need them all because it has commands it needs to render to each of, meaning it's got too much to do and just can't render at your desired frame rate.


If you get nil you need to drop that frame (i.e. only render with the renderPassDescriptor if it's not nil, but move onto the next frame).

    MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;

    if(renderPassDescriptor != nil)
    {

        id  renderEncoder =
            [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

        /// Final pass rendering code here
        ...

        [renderEncoder endEncoding];

        [commandBuffer presentDrawable:view.currentDrawable];
    }

    [commandBuffer commit];

Replies

TL;DR: nil means the GPU is working on too many frames at once so you need skip the frame


It can be nil if CoreAnimation can't provide a drawable which the Metal Kit view needs to set in the currentRenderPassDescriptor. Core Animation may not be able to provide a drawable because drawables are limited and the GPU needs them all. The GPU may need them all because it has commands it needs to render to each of, meaning it's got too much to do and just can't render at your desired frame rate.


If you get nil you need to drop that frame (i.e. only render with the renderPassDescriptor if it's not nil, but move onto the next frame).

    MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;

    if(renderPassDescriptor != nil)
    {

        id  renderEncoder =
            [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

        /// Final pass rendering code here
        ...

        [renderEncoder endEncoding];

        [commandBuffer presentDrawable:view.currentDrawable];
    }

    [commandBuffer commit];

Thanks!

I'm experiencing a similar problem here with the renderPassDescriptor returning nil.
However, I'm seeing that that request can take exactly one full second to time out (both in the docs and anecdotally) which is really putting a wrinkle in things for me. In the process of obtaining the descriptor, a drawable is obtained... and "if there is no drawable available at the time of your request, the calling thread is blocked until a new drawable becomes available (which is usually at the next display refresh interval)." This would affirm the mention in the docs for 'nextDrawable'.
My experience has been that the entire rendering thread blocks (for 1 second) if the renderPassDescriptor is nil, which makes 'skipping to the next frame' rather difficult.

There are only a limited number of drawables avalibel to your app. You may have a reference to drawable that you obtained previously, so CoreAnimation doesn't have another one to give you. If you get nil, make sure your previous command buffers using drawables have all been commited.

Dan, can you confirm that there's a limited number of drawables per app.


Apple documentation seems to indicate that there are a limited number of drawables per CAMetalLayer (see "maximumDrawableCount" method) and that the designation isn't particularly per app:

https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount?language=objc


What if my app has up to a dozen separate layers and/or MTKViews? Do I have to coordinate their drawing so that the use of the underlying drawables is very staggered?