Problem description
My program uses MTLBuffer
to allocate some memory on the GPU and do computations with it. Then I need to copy the result to a specific place on the host. All solutions I found on the internet involve synchronizing the buffer firstly and then copying it to the place I need. Is there a way to copy data from MTLBuffer
directly to the host buffer?
Sample code
Current implementation:
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];
}
What I'm looking for:
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];
}
Additional info
- The size of the buffer transferred to the CPU is about 80 MB in most cases.
hostBuff
points to a piece of 16-byte aligned memory.hostBuff
is a generic buffer. It comes from the outside, so I can't be sure that it was allocated withmmap()
.
CodePudding user response:
Answering your direct question: there's no way to copy MTLBuffer
into a host pointer. I think the reason comes down to how virtual memory is mapped for CPU and GPU. And even if there was one, it would be identical to how managed buffers work, because the command you encode into a command encoder is executed on GPU timeline, which means even after calling that command method, you wouldn't see the result in the host pointer unless you end the encoding on a command encoder and then commit
the command buffer and then wait for it to complete. There is however a method for copying between two MTLBuffer
s: -[MTLBlitCommandEncoder copyFromBuffer:sourceOffset:toBuffer:destinationOffset:size:]
.
But if you are using managed buffers, there's another way to do it. You can just call -[MTLBlitCommandEncoder synchronizeResource:]
to make the changed made on GPU timeline visible and read the contents of it through -[MTLBuffer contents]
.
Also, if you are always reading it back on the CPU and the amount of data is relatively small, you might just as well be using shared storage mode.
There are two articles that go more in depth on chosing a storage mode: Choosing a Resource Storage Mode in iOS and tvOS and Choosing a Resource Storage Mode in macOS