I am at the moment porting a videogame console emulator to iOS and I am attempting to make the renderer (examined on MacOS) work on iOS as properly.
The emulator core is written in C++ and makes use of metal-cpp for rendering, whereas the iOS frontend is written in Swift with SwiftUI. I’ve an Goal-C++ bridging header for bridging the Swift and C++ sides.
On the Swift aspect, I create an MTKView. Contained in the MTKView delegate, I run the emulator for 1 video body and cross it the view’s backing layer for it to render the ultimate output picture with. The emulator runs and returns, however when it returns I get a crash in Swift land (callstack hooked up beneath), inside objc_release, which signifies I am doing one thing flawed with reminiscence administration.
My bridging interface (ios_driver.h):
#pragma as soon as
#embody
#embody
void iosCreateEmulator();
void iosRunFrame(CAMetalLayer* layer);
Bridge implementation (ios_driver.mm):
#import
extern "C" {
#embody "ios_driver.h"
}
<...>
#outline IOS_EXPORT extern "C" __attribute__((visibility("default")))
std::unique_ptr emulator = nullptr;
IOS_EXPORT void iosCreateEmulator() { ... }
// Runs 1 video body of the emulator and
IOS_EXPORT void iosRunFrame(CAMetalLayer* layer) {
void* layerBridged = (__bridge void*)layer;
// Go the CAMetalLayer to the emulator
emulator->getRenderer()->setMTKLayer(layerBridged);
// Runs the emulator for 1 body and renders the output picture utilizing our layer
emulator->runFrame();
}
My MTKView delegate:
class Renderer: NSObject, MTKViewDelegate {
var dad or mum: ContentView
var gadget: MTLDevice!
init(_ dad or mum: ContentView) {
self.dad or mum = dad or mum
if let gadget = MTLCreateSystemDefaultDevice() {
self.gadget = gadget
}
tremendous.init()
}
func mtkView(_ view: MTKView, drawableSizeWillChange dimension: CGSize) {}
func draw(in view: MTKView) {
var metalLayer = view.layer as! CAMetalLayer
// Run the emulator for 1 body & show the output picture
iosRunFrame(metalLayer)
}
}
Lastly, the emulator’s render perform that interacts with the layer:
void RendererMTL::setMTKLayer(void* layer) {
metalLayer = (CA::MetalLayer*)layer;
}
void RendererMTL::show() {
CA::MetalDrawable* drawable = metalLayer->nextDrawable();
if (!drawable) {
return;
}
MTL::Texture* texture = drawable->texture();
}
That is the Swift callstack on the time of the crash:
To my understanding, I should not be violating ARC guidelines as my bridging header makes use of CAMetalLayer*
as a substitute of void*
and Swift will mechanically account for ARC when passing CoreFoundation objects to Goal-C. Nevertheless I haven’t got every other thought as to what is perhaps inflicting this. I have been attempting to debug this code for a few days with out a lot success.
In case you want extra data, the emulator code can also be on Github
Metallic renderer: https://github.com/wheremyfoodat/Panda3DS/blob/ios/src/core/renderer_mtl/renderer_mtl.cpp#L58-L68
Bridge implementation: https://github.com/wheremyfoodat/Panda3DS/blob/ios/src/ios_driver.mm
Bridging header: https://github.com/wheremyfoodat/Panda3DS/blob/ios/embody/ios_driver.h
Different issues I’ve tried:
-
I used to be initially utilizing a
void*
as a substitute of aCAMetalLayer*
within the bridging interface however that crashed in the same manner & made reminiscence administration much more complicated so I dropped it. -
Changing iosRunFrame’s code with only a
printf
would not result in a crash, the C++ aspect returns and the app retains operating. Nevertheless, that is not very useful since I can not see something… -
Operating the emulator with out rendering (utilizing its null renderer) additionally works. I get the right print messages within the XCode terminal & I can hear the sport audio.
-
I’ve tried a number of different methods of passing the issues I must render from Swift to C++, to no avail, all of them crash in objc_release in a similar way (I’ve tried passing a MetalDrawable* as a substitute of the CAMetalLayer itself for instance)
Any assistance is greater than appreciated. Thanks in your time prematurely.