Nate Hat
Nate Hat

Reputation: 418

- [CAMetalLayer nextDrawable] issue on OSX 10.11 Beta 2

Whenever I add the CAMetalLayer to the NSView, the [CAMetalLayer nextDrawable] method will pass nil after 3 successful id<CAMetalDrawable>.

I tried two different ways to configure the setup. One, I used MTKView and used its CAMetalLayer, it didn't work. Second, used NSView and created new CAMetalLayer. That didn't work also. I had weird issues.

I want to know anyone else is having this problem and if anyone know a solution to resolve this.

Additional notes:
I don't want to use MTKView draw system by overriding its methods (not yet). Also this is not a problem on iOS 8 and I didn't tried my code with the beta release of iOS 9 (not yet).

Update

I reroute my drawable calls to use the MTKViewDelegate delegate. And from the drawInView delegate method, I was able to retrieve consistent drawable frames. However, I still would like to use the nextDrawable method directly from CAMetalLayer. Hope this helps anyone else.

Upvotes: 3

Views: 2266

Answers (3)

George
George

Reputation: 949

Use @autoreleasepool for render in drawable.

https://forums.developer.apple.com/thread/15102

Upvotes: 0

Nate Hat
Nate Hat

Reputation: 418

I forgot to come back to this.

This was fixed with OSX Beta 4. nextDrawable method is working correctly and passing back usable CAMetalDrawable objects. I guess I should've waited until the release version came out before posting this. I just wanted to let everyone else know about this problem back when the beta version was first released.

Upvotes: 0

haawa
haawa

Reputation: 3104

I had this same issue, and asked Metal devs at WWDC 15.

How MTKView works: MTKView has limited number of drawables (probably 3), so when you are encoding frames there are few drawables you can draw to.

What you are doing: Your scene is probably quite simple, so you CPU can encode frames really fast. So it seems like, when CPU is 4 frames ahead of GPU, you ask for next drawable and since all (3) drawables are in use, it fails.

Solution: You need to use semapthore to wait for drawable when there is no free one.

Here's code to use:

let inflightSemaphore = dispatch_semaphore_create(3)

func drawInView(view: MTKView) {

        // use semaphore to encode 3 frames ahead
        dispatch_semaphore_wait(inflightSemaphore, DISPATCH_TIME_FOREVER)

        self.update()

        let commandBuffer = commandQueue.commandBuffer()
        let renderEncoder = commandBuffer.renderCommandEncoderWithDescriptor(view.currentRenderPassDescriptor!)
        renderEncoder.drawPrimitives()


        // use completion handler to signal the semaphore when this frame is completed allowing the encoding of the next frame to proceed
        commandBuffer.addCompletedHandler{ [weak self] commandBuffer in
            if let strongSelf = self {
                dispatch_semaphore_signal(strongSelf.inflightSemaphore)
            }
            return
        }

        commandBuffer.presentDrawable(view.currentDrawable!)
        commandBuffer.commit()
    }

This is not documented anywhere! The only written mention of that is in iOS project template (File -> New -> Project -> Game -> Pick Metal) in GameViewController.

I already filled a radar on this (no response yet), and would appreciate if you do the same https://bugreport.apple.com

Also you may find useful my github repo https://github.com/haawa799/RamOnMetal

Upvotes: 4

Related Questions