ios - How to properly pass a Metal layer from SwiftUI MTKView to C++ for use with metal-cpp? - Stack Overflow

I'm currently porting a videogame console emulator to iOS and I'm trying to make the renderer

I'm currently porting a videogame console emulator to iOS and I'm trying to make the renderer (tested on MacOS) work on iOS as well.

The emulator core is written in C++ and uses metal-cpp for rendering, whereas the iOS frontend is written in Swift with SwiftUI. I have an Objective-C++ bridging header for bridging the Swift and C++ sides.

On the Swift side, I create an MTKView. Inside the MTKView delegate, I run the emulator for 1 video frame and pass it the view's backing layer for it to render the final output image with. The emulator runs and returns, but when it returns I get a crash in Swift land (callstack attached below), inside objc_release, which indicates I'm doing something wrong with memory management.

My bridging interface (ios_driver.h):

#pragma once
#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h>

void iosCreateEmulator();
void iosRunFrame(CAMetalLayer* layer);

Bridge implementation (ios_driver.mm):

#import <Foundation/Foundation.h>

extern "C" {
#include "ios_driver.h"
}

<...>

#define IOS_EXPORT extern "C" __attribute__((visibility("default")))

std::unique_ptr<Emulator> emulator = nullptr;
IOS_EXPORT void iosCreateEmulator() { ... }

// Runs 1 video frame of the emulator and 
IOS_EXPORT void iosRunFrame(CAMetalLayer* layer) {
    void* layerBridged = (__bridge void*)layer;

    // Pass the CAMetalLayer to the emulator
    emulator->getRenderer()->setMTKLayer(layerBridged);
    // Runs the emulator for 1 frame and renders the output image using our layer
    emulator->runFrame();
}

My MTKView delegate:

class Renderer: NSObject, MTKViewDelegate {
    var parent: ContentView
    var device: MTLDevice!
    
    init(_ parent: ContentView) {
        self.parent = parent
        if let device = MTLCreateSystemDefaultDevice() {
            self.device = device
        }
        
        super.init()
    }
    
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
    
    func draw(in view: MTKView) {
        var metalLayer = view.layer as! CAMetalLayer
        // Run the emulator for 1 frame & display the output image
        iosRunFrame(metalLayer)
    }
}

Finally, the emulator's render function that interacts with the layer:

void RendererMTL::setMTKLayer(void* layer) {
    metalLayer = (CA::MetalLayer*)layer;
}

void RendererMTL::display() {
    CA::MetalDrawable* drawable = metalLayer->nextDrawable();
    if (!drawable) {
        return;
    }

    MTL::Texture* texture = drawable->texture();
   <rest of rendering follows here using the drawable & its texture>
}

This is the Swift callstack at the time of the crash:

To my understanding, I shouldn't be violating ARC rules as my bridging header uses CAMetalLayer* instead of void* and Swift will automatically account for ARC when passing CoreFoundation objects to Objective-C. However I don't have any other idea as to what might be causing this. I've been trying to debug this code for a couple of days without much success.

If you need more info, the emulator code is also on Github

Metal renderer: .cpp#L58-L68

Bridge implementation: .mm

Bridging header: .h

Other things I've tried:

  • I was initially using a void* instead of a CAMetalLayer* in the bridging interface but that crashed in a similar way & made memory management even more confusing so I dropped it.

  • Replacing iosRunFrame's code with just a printf doesn't lead to a crash, the C++ side returns and the app keeps running. However, that's not very helpful since I can't see anything...

  • Running the emulator without rendering (using its null renderer) also works. I get the correct print messages in the XCode terminal & I can hear the game audio.

  • I've tried a few other ways of passing the things I need to render from Swift to C++, to no avail, all of them crash in objc_release in a similar fashion (I've tried passing a MetalDrawable* instead of the CAMetalLayer itself for example)

Any help is more than appreciated. Thank you for your time in advance.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744878877a4598716.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信