如何在 C++ 代码中存储 id<MTLBuffer>?
How can I store a id<MTLBuffer> inside of c++ code?
我正在编写一个跨平台引擎来做一些渲染。我正在尝试创建一个跨平台结构,它本质上管理一种绘制的几何体。
目标是有一个 c++ class 将 void* 保存到分配给缓冲区的内存块,然后将该指针传递到 MTLBuffer 或 Vulcan 缓冲区以用于渲染。因此,class 的其中一个字段将需要成为一个缓冲区,但在跨平台的意义上。
对于我绘图的一部分,代码应该如下所示
func draw() {
CrossPlatformBuffers* buffs = // preset list
for (buffer in buffs {
PlatformSpecificEngine.drawWith((PlatformSpecificBuffer)buffs->buffer)
}
}
所以基本上我需要能够将我的 MTLBuffer 存储为 c++ class 中的 void*。这让我感到困惑,因为我不确定 c++ 如何与 objective-c ARC 一起玩,或者 id 到底是什么意思。
如果我只是将 id 转换为 void* 并将其传递到 c++ class as 并稍后对其调用 delete,我会遇到任何问题吗?
在我看来你使用了错误的抽象。这通常是通过使用一个基础 class 来解决的,它在高层次上实现了你想做的事情,并为每个执行特定工作的平台提供了一个子class。所以在你的情况下,你可能有类似 Renderer base class:
的东西
class Renderer {
public:
Renderer();
~Renderer();
virtual void* allocateBuffer(const size_t numBytes) = 0;
virtual void renderWorld() = 0;
... etc.
};
那么你将有 2 个特定于平台的 classes: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
class 的实现文件将是 .mm 文件而不是 .cpp 文件,表明它是一个 Objective-C++ 文件并允许 C++ 对象包含 Objective-C 个对象。
None 你的其他代码应该处理 MetalRenderer
或 VulkanRenderer
而不是 Renderer
。
这里有几点你需要考虑:
您的 MTLBuffer object 指针不指向object的内容
MTLBuffer
是一个 Metal 框架 object,它管理 GPU 上的内存缓冲区。您拥有的地址就是那个 object 的地址。对于某些缓冲区,Metal 提供了一种使用 [MTLBuffer contents]
方法从 CPU 访问缓冲区内容的方法。 contents
returns 一个 void *
可以直接用于从缓冲区读取和写入,但有以下注意事项:
您的 MTLBuffer 内容可能并不总是可以从 CPU
访问
这取决于您使用的平台。如果您只使用 iOS/tvOS,只需使用 MTLStorageModeShared
存储模式创建您的 MTLBuffer
,您就可以开始了——Metal 将确保您在 [=] 上看到的数据98=] 与 GPU 的视图同步。在 macOS 上,它取决于您使用的是哪个 GPU,因此还有一些其他的细微之处。现在我假设我们只讨论 iOS/tvOS。
有几种方法可以将Objective-C与C++代码结合
一种选择是创建 Objective-C++ 文件(.mm 文件),然后将所有 Metal-specific 代码放入这些 .mm 文件中的子 class 中。这将允许您享受 Objective-C 的 ARC(自动引用计数)的好处,并且仍然创建漂亮的通用 C++ 包装器。在 Objective-C++ class 的 header 文件中,您可以这样做:
class MetalBuffer : GenericBuffer
{
private:
#ifdef __OBJC__
id <MTLBuffer> metalBuffer;
#else
void *internalMetalBuffer;
#endif
}
这将使您的常规(非Objective-C++)classes 包含Objective-C++ header。我制作了 "special" 托管指针 private
以确保没有人试图从 Objective-C++ class 外部访问它,因为这显然不是一个好主意。
如果这种方法不合适,您可以在 C++ 中完成所有操作,但是您必须手动跟踪对您的 Objective-C objects:
的引用
如果您必须将 object 作为空指针存储在 C++ 代码中,您将需要手动引用计数
Objective-C 使用 ARC 来跟踪 object 使用情况并在适当时自动释放 object。如果您试图在 C++ 中管理整个事情,您将需要手动管理对您的 object 的引用(例如您的 MTLBuffer
及以后)。这是通过告诉 ARC 您希望 type-cast 托管的 Objective-C id
object 到常规 C 指针来完成的。
实例化 MTLBuffer
后,您可以在 object 上使用 CFBridgingRetain()
,现在可以将其存储为 void *
(不要与void *
你在你的 C++ class 中抓住了指向缓冲区 内容 的那个!)。使用完 MTLBuffer
后,您可以在 void *
上调用 CFRelease()
来释放它。您无需担心释放 contents
缓冲区——一旦 MTLBuffer
object 被释放(例如当您调用 CFRelease()
时),底层内存将自动释放.
请注意,当您需要调用使用您的 MTLBuffer
object(例如 setFragmentBuffer
等)的 Objective-C 函数时,您可以使用 CFBridgingRelease()
。 ) 将 CFBridgingRelease()
视为将您的 object 返回给 ARC 的转换器,但请注意它包含一个手动释放,这意味着一旦 Metal 完成了您的 object,它将自动被发布。
如果你想让你的 object 在当前 Metal 请求之后继续存在,你应该使用 CFBridgingRetain()
保留另一个指向它的指针。
同样,这是最后的手段 -- 我不建议走这条路。
祝你好运!
我正在编写一个跨平台引擎来做一些渲染。我正在尝试创建一个跨平台结构,它本质上管理一种绘制的几何体。
目标是有一个 c++ class 将 void* 保存到分配给缓冲区的内存块,然后将该指针传递到 MTLBuffer 或 Vulcan 缓冲区以用于渲染。因此,class 的其中一个字段将需要成为一个缓冲区,但在跨平台的意义上。
对于我绘图的一部分,代码应该如下所示
func draw() {
CrossPlatformBuffers* buffs = // preset list
for (buffer in buffs {
PlatformSpecificEngine.drawWith((PlatformSpecificBuffer)buffs->buffer)
}
}
所以基本上我需要能够将我的 MTLBuffer 存储为 c++ class 中的 void*。这让我感到困惑,因为我不确定 c++ 如何与 objective-c ARC 一起玩,或者 id 到底是什么意思。
如果我只是将 id 转换为 void* 并将其传递到 c++ class as 并稍后对其调用 delete,我会遇到任何问题吗?
在我看来你使用了错误的抽象。这通常是通过使用一个基础 class 来解决的,它在高层次上实现了你想做的事情,并为每个执行特定工作的平台提供了一个子class。所以在你的情况下,你可能有类似 Renderer base class:
的东西class Renderer {
public:
Renderer();
~Renderer();
virtual void* allocateBuffer(const size_t numBytes) = 0;
virtual void renderWorld() = 0;
... etc.
};
那么你将有 2 个特定于平台的 classes: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
class 的实现文件将是 .mm 文件而不是 .cpp 文件,表明它是一个 Objective-C++ 文件并允许 C++ 对象包含 Objective-C 个对象。
None 你的其他代码应该处理 MetalRenderer
或 VulkanRenderer
而不是 Renderer
。
这里有几点你需要考虑:
您的 MTLBuffer object 指针不指向object的内容
MTLBuffer
是一个 Metal 框架 object,它管理 GPU 上的内存缓冲区。您拥有的地址就是那个 object 的地址。对于某些缓冲区,Metal 提供了一种使用 [MTLBuffer contents]
方法从 CPU 访问缓冲区内容的方法。 contents
returns 一个 void *
可以直接用于从缓冲区读取和写入,但有以下注意事项:
您的 MTLBuffer 内容可能并不总是可以从 CPU
访问这取决于您使用的平台。如果您只使用 iOS/tvOS,只需使用 MTLStorageModeShared
存储模式创建您的 MTLBuffer
,您就可以开始了——Metal 将确保您在 [=] 上看到的数据98=] 与 GPU 的视图同步。在 macOS 上,它取决于您使用的是哪个 GPU,因此还有一些其他的细微之处。现在我假设我们只讨论 iOS/tvOS。
有几种方法可以将Objective-C与C++代码结合
一种选择是创建 Objective-C++ 文件(.mm 文件),然后将所有 Metal-specific 代码放入这些 .mm 文件中的子 class 中。这将允许您享受 Objective-C 的 ARC(自动引用计数)的好处,并且仍然创建漂亮的通用 C++ 包装器。在 Objective-C++ class 的 header 文件中,您可以这样做:
class MetalBuffer : GenericBuffer
{
private:
#ifdef __OBJC__
id <MTLBuffer> metalBuffer;
#else
void *internalMetalBuffer;
#endif
}
这将使您的常规(非Objective-C++)classes 包含Objective-C++ header。我制作了 "special" 托管指针 private
以确保没有人试图从 Objective-C++ class 外部访问它,因为这显然不是一个好主意。
如果这种方法不合适,您可以在 C++ 中完成所有操作,但是您必须手动跟踪对您的 Objective-C objects:
的引用如果您必须将 object 作为空指针存储在 C++ 代码中,您将需要手动引用计数
Objective-C 使用 ARC 来跟踪 object 使用情况并在适当时自动释放 object。如果您试图在 C++ 中管理整个事情,您将需要手动管理对您的 object 的引用(例如您的 MTLBuffer
及以后)。这是通过告诉 ARC 您希望 type-cast 托管的 Objective-C id
object 到常规 C 指针来完成的。
实例化 MTLBuffer
后,您可以在 object 上使用 CFBridgingRetain()
,现在可以将其存储为 void *
(不要与void *
你在你的 C++ class 中抓住了指向缓冲区 内容 的那个!)。使用完 MTLBuffer
后,您可以在 void *
上调用 CFRelease()
来释放它。您无需担心释放 contents
缓冲区——一旦 MTLBuffer
object 被释放(例如当您调用 CFRelease()
时),底层内存将自动释放.
请注意,当您需要调用使用您的 MTLBuffer
object(例如 setFragmentBuffer
等)的 Objective-C 函数时,您可以使用 CFBridgingRelease()
。 ) 将 CFBridgingRelease()
视为将您的 object 返回给 ARC 的转换器,但请注意它包含一个手动释放,这意味着一旦 Metal 完成了您的 object,它将自动被发布。
如果你想让你的 object 在当前 Metal 请求之后继续存在,你应该使用 CFBridgingRetain()
保留另一个指向它的指针。
同样,这是最后的手段 -- 我不建议走这条路。
祝你好运!