我正在编写一个跨平台引擎来进行渲染。我正在尝试创建一个跨平台结构,该结构本质上管理绘制的几何类型。
目标是存在一个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,我会遇到任何问题吗?
答案 0 :(得分:5)
这里有几点需要考虑:
您的MTLBuffer对象指针 not 指向对象的内容
MTLBuffer
是一个Metal框架对象,用于管理GPU上的内存缓冲区。您拥有的地址就是该对象的地址。对于某些缓冲区,Metal提供了一种使用[MTLBuffer contents]
方法从CPU访问缓冲区内容的方法。 contents
返回一个void *
,您可以直接使用它来从缓冲区读取和写入,但要注意以下几点:
您的MTLBuffer的内容可能并不总是可以通过CPU访问
这取决于您所使用的平台。如果您仅在iOS / tvOS上运行,只需使用MTLBuffer
存储模式创建MTLStorageModeShared
,然后就可以开始使用-Metal将确保您在CPU上看到的数据是同步的与GPU的视图。在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
}
这将使您的常规(非Object-C ++)类包含Objective-C ++头文件。我制作了“特殊”托管指针private
以确保没有人尝试从Objective-C ++类外部访问它,因为这显然不是一个好主意。
如果这种方法不合适,则可以使用C ++进行所有操作,但随后必须手动跟踪对Objective-C对象的引用:
如果必须将对象作为空指针存储在C ++代码中,则需要手动进行引用计数
Objective-C使用ARC来跟踪对象使用情况并适当地自动释放对象。如果您要管理C ++中的全部内容,则需要手动管理对您对象的引用(例如MTLBuffer
及其他对象)。这是通过告诉ARC您希望将托管的Objective-C id
对象类型转换为常规C指针来完成的。
实例化MTLBuffer
之后,可以在对象上使用CFBridgingRetain()
,现在可以将其存储为void *
(不要与void *
混淆)抓住了指向C ++类中缓冲区的内容!使用完MTLBuffer
后,可以在CFRelease()
上调用void *
释放它。您无需担心释放contents
缓冲区的情况-释放MTLBuffer
对象后(例如,调用CFRelease()
时),基础内存将自动释放。
请注意,当您需要调用使用CFBridgingRelease()
对象(例如MTLBuffer
等)的Objective-C函数时,可以使用setFragmentBuffer
作为将您的对象交还给ARC的转换器,但是请注意,它包括手动释放,这意味着一旦对您的对象完成了Metal操作,它将自动释放。
如果您希望对象超出当前的Metal请求,则应使用CFBridgingRelease()
保留指向该对象的另一个指针。
同样,这是不得已的做法-我不建议您选择这条路线。
祝你好运!
答案 1 :(得分:1)
在我看来,您使用的是错误的抽象。这通常可以通过使用一个基类来解决,该基类可以高层地实现您想要做的事情,并为每个平台执行特定的工作提供一个子类。因此,在您的情况下,您可能会有类似Renderer基类的内容:
class Renderer {
public:
Renderer();
~Renderer();
virtual void* allocateBuffer(const size_t numBytes) = 0;
virtual void renderWorld() = 0;
... etc.
};
那么您将拥有2个特定于平台的类:VulkanRenderer
和MetalRenderer
:
class VulkanRenderer: public Renderer {
public:
VulkanRenderer();
~VulkanRenderer();
virtual void* allocateBuffer(const size_t numBytes);
virtual void renderWorld();
... etc.
};
和
class MetalRenderer: public Renderer {
public:
MetalRenderer();
~MetalRenderer();
virtual void* allocateBuffer(const size_t numBytes);
virtual void renderWorld();
... etc.
};
您的MetalRenderer
类的实现文件将是.mm文件而不是.cpp文件,表明它是一个Objective-C ++文件,并允许C ++对象包含Objective-C对象。
您的其他任何代码都不应处理MetalRenderer
或VulkanRenderer
,而只能处理Renderer
。