November 06, 2023

I've come to announce that, by following some old code on arsd:simpledisplay (pre v11.0.0), Apple's documentation and this amazing contribution from Jacob - http://www.dsource.org/projects/dstep/wiki/ObjcBridge/BridgeInternals

I've removed all extern(Objective-C) code from the metal bindings, and now, all of the Objective-C bridge code is generated by using D's reflection.

So, given an example code like that:

import core.attribute : selector;
extern(Objective-C):
class MTKView
{
    ///Creates a render pass descriptor to draw into the current drawable.
    @selector("currentRenderPassDescriptor")
    MTLRenderPassDescriptor currentRenderPassDescriptor();
}

class MTKView2 : MTKView
{
    ///Creates a render pass descriptor to draw into the current drawable.
    @selector("currentRenderPassDescriptor2")
    MTLRenderPassDescriptor currentRenderPassDescriptor2();
}

We have now:

mtkview.d

module mtkview;
import objc.meta : selector;
@ObjectiveC final extern(C++):

class MTKView
{
    ///Creates a render pass descriptor to draw into the current drawable.
    @selector("currentRenderPassDescriptor")
    MTLRenderPassDescriptor currentRenderPassDescriptor();
}

class MTKView2
{
    mixin ObjcExtends!(MTKView);
    ///Creates a render pass descriptor to draw into the current drawable.
    @selector("currentRenderPassDescriptor2")
    MTLRenderPassDescriptor currentRenderPassDescriptor2();
}

metal_gen.d

module metal_gen;
import mtkview;
mixin ObjcLinkModule!(mtkview);

This is an example on how to actually get advantages from separate compilation, compiling reflection code separately from the interface code can lead to a lot better compilation speed.

The mixin ObjcLinkModule is used to iterate every @ObjectiveC UDA, generating the implementation for each method defined in the classes and interfaces. (Yes, you can implement the class methods in other file).
The final is used to make D avoid virtual tables, since Objective C objects doesn't follow D convention, this is needed to get a namespace and a pointer. (With that, it is possible even to make this valid code which doesn't segfault:

import mtkview;
(cast(MTKView)(new int)).currentRenderPassDescriptor;

While extern(C++) is basically some way to save some memory by not generating TypeInfo and also, avoiding D dynamic casts, whenever you need to cast, since they would give a segmentation fault.

Although the interfacing code is not as clean as it was before, for the user, the only thing that changes is that since all classes are final, they aren't implicitly casted to their parent class, so, an explicit cast is needed. It is a reasonable trade for having support for iOS and M1, I guess. Though that happens, in my entire engine I needed to fix like 2 lines of code after all the change I've made to the binding, so, the front user will still have a good looking code.

D Metal Binding | Dub Package

With all of this done, in the near future, iOS support, with official LDC support on build selector for MacOS, will be done for Hipreme Engine, which will make the engine supporting literally every platform available for the common users :)

November 09, 2023

Looking forward to supporting iOS! ;)