MTLBuffer 将数据直接从设备复制到主机上的特定缓冲区
MTLBuffer copy data to a specific buffer on host directly from the device
问题描述
我的程序使用 MTLBuffer
在 GPU 上分配一些内存并用它进行计算。然后我需要将结果复制到主机上的特定位置。我在互联网上找到的所有解决方案都是先同步缓冲区,然后将其复制到我需要的地方。有没有办法将数据从 MTLBuffer
直接复制到主机缓冲区?
示例代码
当前实施:
void ComputeOnGPU(void* hostBuff, size_t buffSize)
{
id<MTLBuffer> gpuBuff = [device newBufferWithLength: buffSize
options: MTLResourceStorageModeManaged];
//
// Do stuff with the GPU buffer
//
id<MTLCommandQueue> commandQueue = [device newCommandQueue];
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
[blitEncoder synchronizeResource: gpuBuff];
[blitEncoder endEncoding];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
std::memcpy(hostBuff, [gpuBuff contents], buffSize);
[gpuBuff setPurgeableState: MTLPurgeableStateEmpty];
[gpuBuff release];
}
我在找什么:
void ComputeOnGPU(void* hostBuff, size_t buffSize)
{
id<MTLBuffer> gpuBuff = [device newBufferWithLength: buffSize
options: MTLResourceStorageModeManaged];
//
// Do stuff with the GPU buffer
//
id<MTLCommandQueue> commandQueue = [device newCommandQueue];
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
// Is there something like that?
[blitEncoder copyMemoryFromBuffer: gpuBuff
toHost: hostBuff];
[blitEncoder endEncoding];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
[gpuBuff setPurgeableState: MTLPurgeableStateEmpty];
[gpuBuff release];
}
附加信息
- 传输到 CPU 的缓冲区大小超过 4KB。
hostBuff
是一个通用缓冲区。它来自外部,所以我不能确定它是用 mmap()
. 分配的
回答您的直接问题:无法将 MTLBuffer
复制到主机指针中。我认为原因归结为如何为 CPU 和 GPU 映射虚拟内存。即使有一个,它也与托管缓冲区的工作方式相同,因为您编码到命令编码器中的命令是在 GPU 时间轴上执行的,这意味着即使在调用该命令方法之后,您也不会在主机指针,除非您在命令编码器上结束编码,然后 commit
命令缓冲区,然后等待它完成。然而,有一种在两个 MTLBuffer
之间复制的方法:-[MTLBlitCommandEncoder copyFromBuffer:sourceOffset:toBuffer:destinationOffset:size:]
.
但是如果您使用托管缓冲区,还有另一种方法可以做到这一点。您只需调用 -[MTLBlitCommandEncoder synchronizeResource:]
即可使 GPU 时间轴上的更改可见,并通过 -[MTLBuffer contents]
.
读取其中的内容
另外,如果你总是在CPU上回读,而且数据量比较小,不妨使用共享存储模式。
有两篇文章更深入地介绍了选择存储模式:在 iOS 和 tvOS 中选择资源存储模式
和 在 macOS 中选择资源存储模式
问题描述
我的程序使用 MTLBuffer
在 GPU 上分配一些内存并用它进行计算。然后我需要将结果复制到主机上的特定位置。我在互联网上找到的所有解决方案都是先同步缓冲区,然后将其复制到我需要的地方。有没有办法将数据从 MTLBuffer
直接复制到主机缓冲区?
示例代码
当前实施:
void ComputeOnGPU(void* hostBuff, size_t buffSize)
{
id<MTLBuffer> gpuBuff = [device newBufferWithLength: buffSize
options: MTLResourceStorageModeManaged];
//
// Do stuff with the GPU buffer
//
id<MTLCommandQueue> commandQueue = [device newCommandQueue];
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
[blitEncoder synchronizeResource: gpuBuff];
[blitEncoder endEncoding];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
std::memcpy(hostBuff, [gpuBuff contents], buffSize);
[gpuBuff setPurgeableState: MTLPurgeableStateEmpty];
[gpuBuff release];
}
我在找什么:
void ComputeOnGPU(void* hostBuff, size_t buffSize)
{
id<MTLBuffer> gpuBuff = [device newBufferWithLength: buffSize
options: MTLResourceStorageModeManaged];
//
// Do stuff with the GPU buffer
//
id<MTLCommandQueue> commandQueue = [device newCommandQueue];
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
// Is there something like that?
[blitEncoder copyMemoryFromBuffer: gpuBuff
toHost: hostBuff];
[blitEncoder endEncoding];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
[gpuBuff setPurgeableState: MTLPurgeableStateEmpty];
[gpuBuff release];
}
附加信息
- 传输到 CPU 的缓冲区大小超过 4KB。
hostBuff
是一个通用缓冲区。它来自外部,所以我不能确定它是用mmap()
. 分配的
回答您的直接问题:无法将 MTLBuffer
复制到主机指针中。我认为原因归结为如何为 CPU 和 GPU 映射虚拟内存。即使有一个,它也与托管缓冲区的工作方式相同,因为您编码到命令编码器中的命令是在 GPU 时间轴上执行的,这意味着即使在调用该命令方法之后,您也不会在主机指针,除非您在命令编码器上结束编码,然后 commit
命令缓冲区,然后等待它完成。然而,有一种在两个 MTLBuffer
之间复制的方法:-[MTLBlitCommandEncoder copyFromBuffer:sourceOffset:toBuffer:destinationOffset:size:]
.
但是如果您使用托管缓冲区,还有另一种方法可以做到这一点。您只需调用 -[MTLBlitCommandEncoder synchronizeResource:]
即可使 GPU 时间轴上的更改可见,并通过 -[MTLBuffer contents]
.
另外,如果你总是在CPU上回读,而且数据量比较小,不妨使用共享存储模式。
有两篇文章更深入地介绍了选择存储模式:在 iOS 和 tvOS 中选择资源存储模式 和 在 macOS 中选择资源存储模式