我正在编写一个跨平台引擎来进行渲染。我正在尝试创建一个跨平台结构,该结构本质上管理绘制的几何类型。

目标是存在一个c++类,该类将void *保留给分配给缓冲区的内存块,然后将该指针传递到MTLBuffer或Vulcan Buffer中以进行渲染。因此,此类的一个字段将需要是一个缓冲区,但必须是跨平台的。

对于我的绘图的一部分,代码应类似于

func draw() {
   CrossPlatformBuffers* buffs = // preset list
   for (buffer in buffs {
      PlatformSpecificEngine.drawWith((PlatformSpecificBuffer)buffs->buffer)
   }
}

所以本质上,我需要能够将我的MTLBuffer存储为c++类中的void *。这让我感到困惑,因为我不确定c++如何与Objective-C ARC配合使用,或者id到底意味着什么。

如果我将id强制转换为void *并将其传递给c++类,然后在其上调用delete,将会遇到任何问题吗?

最佳答案

您需要在此处考虑以下几点:

您的MTLBuffer对象指针未指向该对象的内容
MTLBuffer是一个Metal框架对象,用于管理GPU上的内存缓冲区。您拥有的地址就是该对象的地址。对于某些缓冲区,Metal提供了一种使用[MTLBuffer contents]方法从CPU访问缓冲区内容的方法。 contents返回一个void *,您可以将其直接用于从缓冲区读取和写入,但要注意以下几点:

您的MTLBuffer的内容可能并不总是可以从CPU上访问

这取决于您所使用的平台。如果您仅在iOS / tvOS上运行,只需使用MTLBuffer存储模式创建MTLStorageModeShared,就可以了-金属将确保您在CPU上看到的数据与GPU的 View 保持同步。在macOS上,这取决于您所使用的GPU,因此那里还有其他一些细微之处。现在,我假设我们只在谈论iOS / tvOS。

有几种方法可以将Objective-C与C++代码结合在一起

一种选择是创建Objective-C++文件(.mm文件),并将所有特定于Metal的代码放在这些.mm文件中的子类中。这将使您能够受益于Objective-C的ARC(自动引用计数),并且仍然可以创建漂亮的通用C++包装器。在Objective-C++类的头文件中,您将执行以下操作:

class MetalBuffer : GenericBuffer
{
   private:
#ifdef __OBJC__
      id <MTLBuffer> metalBuffer;
#else
      void *internalMetalBuffer;
#endif
}

这将使您的常规(非Objective-C++)类包含Objective-C++ header 。我制作了“特殊”托管指针private以确保没有人尝试从Objective-C++类外部访问它,因为这显然是一个坏主意。

如果这种方法不合适,则可以使用C++进行所有操作,但随后必须手动跟踪对Objective-C对象的引用:

如果必须将对象作为空指针存储在C++代码中,则需要手动引用计数

Objective-C使用ARC来跟踪对象的使用情况并适当地自动释放对象。如果您要管理C++中的全部内容,则需要手动管理对您对象的引用(例如MTLBuffer及更高版本)。这是通过告诉ARC您希望将托管的Objective-C id对象类型转换为常规C指针来完成的。

实例化MTLBuffer之后,您可以在对象上使用CFBridgingRetain(),现在可以将其存储为void *(不要与您抓取的指向缓冲区内容的void *混淆!)。使用完MTLBuffer后,可以在CFRelease()上调用void *释放它。您无需担心释放contents缓冲区的问题-释放MTLBuffer对象后(例如,当您调用CFRelease()时),基础内存将自动释放。

请注意,当您需要调用使用CFBridgingRelease()对象的Objective-C函数(例如MTLBuffer等)时,可以使用setFragmentBuffer。将CFBridgingRelease()视为将对象转回ARC的转换器,但请注意,它包括手动发行版,这意味着一旦对您的对象完成Metal处理,它将自动被释放。

如果希望对象超出当前的Metal请求,则应使用CFBridgingRetain()保留指向该对象的另一个指针。

同样,这是不得已的做法-我不建议您选择这条路线。

祝好运!

10-01 13:53