有没有可能直接使用C++与Metal API

问题描述 投票:0回答:5

由于 Metal 语言也基于 C++11,而 C++ 似乎是一个完美的选择,它是一种经过验证的性能语言,我希望完全绕过 Objective-C / Swift。我很想留在 C++ 领域。有没有可能?

c++ ios swift metal
5个回答
10
投票

技术上是可以的,但它会非常丑陋,并且可能会破坏其他一些 Objective-C 部分。

Objective-C 与 C++ 一样,最初是作为预处理器出现的。其遗留问题之一是每个 Objective-C 方法也公开为 C 函数调用,该函数调用分别将对象实例和方法选择器作为前两个参数,然后按从左到右的顺序声明其他参数。

NSObject
和构成 Objective-C 运行时的 C 调用都可以查找任何方法调用都会调用的 current C 函数。

因此,您可以创建一个 C++ 类来获取所有 C 函数指针(它甚至可以从 C 运行时执行此操作,使其成为纯 C++ 代码而不是 Objective-C++),并像这样直接跳转到适当的代码。

缺点是:Objective-C 通常使用动态分派(即每次调用时解析为 C 函数的方法),而像键值观察这样的机制仅因为使用动态分派而起作用。如果您在其他人开始观察之前获取 C 函数指针,那么您的调用将在不通知观察者的情况下更新属性。所以你可能会破坏一些模块。

发出该警告,并假设您可以将 C++ 桥接类构建为 Objective-C++ 以实现更简单的语法,例如

class MTLArray {
    id m_instance;
    static NSUInteger (* s_arrayLength)(id object, SEL selector);
};

MTLArray::MTLArray() {
    // assuming you use the m_ pattern for instance variables
    m_instance = [MTLArray new];

    // assuming you use s_ for static variables; also pretending
    // the mapping from method to C function will never change —
    // KVO is the most prominent exception but otherwise you can
    // be exceedingly confident, albeit you'll be relying on
    // empirical behaviour, not the formal contract
    if(!s_arrayLength) {
        s_arrayLength = [MTLArray instanceMethodForSelector:@selector(arrayLength)];
    }
}

NSUInteger MTLArray::getArrayLength() {
    return s_arrayLength(m_instance, @selector(arrayLength));
}

...我很方便地拒绝将

+instanceMethodForSelector:
的结果转换为适当的类型,因为我确信我会弄错。我倾向于畏缩并在我的实际代码中引入中间
typedef
,但您可能更喜欢更简洁。


3
投票

以下开源项目为 Metal 提供了 C++ 包装器:

https://github.com/naleksiev/mtlpp


1
投票

不。 Metal 仅作为 Objective-C API 公开。最好的情况是,您可以用一组 Objective-C++ 类来包装它,但这只会增加开销,而不是按照您的意愿绕过 Objective-C。


1
投票

如果您想使用 Objective-C API,但主要使用 C++ 工作,那么您最好的选择是 Objective-C++。这只是 Objective-C,但使用 C++ 而不是 C 作为底层语言。

要将 Objective-C 类转换为 Objective-C++,只需将源文件的后缀从“.m”更改为“.mm”即可。 (您还需要确保在链接时包含正确的 C++ 运行时库。)

完成此操作后,您可以在 Objective-C 方法中使用 C++ 代码,包括 std::vector 等模板类型以及基于范围的 for 循环等 C++11 结构。


1
投票

是的! Apple 现在提供官方 Metal C++ 包装器

我最近将自己的游戏开发项目从使用 Objective-C++ 转换为 Metal-cpp,并且性能略有提升。

以下是该转换过程的一些要点:

  • 任何持有原始 metal-cpp 指针的类(在
    MTL
    NS
    CA
    namespace
    )必须显式
    delete
    复制构造函数和复制运算符,或者必须实现其中之一。这将允许编译器告诉您复制发生在哪里以及您可能在哪里执行过度
    release
    导致运行时出现
    EXC_BAD_ACCESS
    异常:
struct TextureUpload {
    MTL::Buffer* srcBuffer;
    MTL::Texture* dstTexture;

    TextureUpload(MTL::Buffer* srcBuffer, MTL::Texture* dstTexture) :
        srcBuffer{ srcBuffer->retain() },
        dstTexture{ dstTexture->retain() } {
    }
    TextureUpload(TextureUpload const& other) :
        srcBuffer{ other.srcBuffer->retain() },
        dstTexture{ other.dstTexture->retain() } {
    }
    TextureUpload& operator=(TextureUpload const&) = delete;
    ~TextureUpload() {
        srcBuffer->release();
        dstTexture->release();
    }
};

或者,您可以使用

NS::SharedPtr
来管理 metal-cpp 对象的生命周期:

struct TextureUpload {
    NS::SharedPtr<MTL::Buffer> srcBuffer;
    NS::SharedPtr<MTL::Texture> dstTexture;

    TextureUpload(MTL::Buffer* srcBuffer, MTL::Texture* dstTexture) :
        srcBuffer{ NS::RetainPtr(srcBuffer) },
        dstTexture{ NS::RetainPtr(dstTexture) } {
    }
};
  • MTKViewDelegate
    的实现需要使用Objective-C++,为了将
    MTKView
    currentRenderPassDescriptor
    currentDrawable
    传递给纯C++,并且尽可能晚,我使用了回调机制:

MetalMain.hpp
中(
MetalMain
是我执行实际渲染的纯C++类):

using GetRenderPassDescriptor = std::function<MTL::RenderPassDescriptor*(void)>;
using GetDrawable = std::function<CA::MetalDrawable*(void)>;

MTKViewDelegate
drawInMTKView:
实施:

- (void)drawInMTKView:(nonnull MTKView*)view {
    MetalMain::GetRenderPassDescriptor getRenderPassDescriptor = [&view]() {
        return (__bridge MTL::RenderPassDescriptor*)view.currentRenderPassDescriptor;
    };

    MetalMain::GetDrawable getDrawable = [&view]() {
        return (__bridge CA::MetalDrawable*)view.currentDrawable;
    };

    _metalMain->draw(getRenderPassDescriptor, getDrawable);
}

摘自

MetalMain::draw()
方法:

__block dispatch_semaphore_t block_sema = _inFlightSemaphore;
commandBuffer->addCompletedHandler(^void(MTL::CommandBuffer* commandBuffer) {
    dispatch_semaphore_signal(block_sema);
});

MTL::RenderPassDescriptor* renderPassDescriptor = getRenderPassDescriptor();
if (renderPassDescriptor != nullptr) {
    // Render
}

commandBuffer->presentDrawable(getDrawable());
commandBuffer->commit();
  • 当我最初实现上述解决方案时,我在引用视图的渲染通道描述符时遇到了崩溃,直到我关闭 ARC 并在整个项目中使用 MRR 后才解决该问题。这没什么大不了的,因为项目中并没有太多 Objective-C++。
© www.soinside.com 2019 - 2024. All rights reserved.