我正在编写一个跨平台引擎来进行渲染。我正在尝试创建一个跨平台结构,该结构本质上管理绘制的几何类型。
目标是存在一个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()
保留指向该对象的另一个指针。同样,这是不得已的做法-我不建议您选择这条路线。
祝好运!