问题描述
我的程序使用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中选择资源存储模式