Моя программа использует MTLBuffer
для выделения некоторой памяти на графическом процессоре и выполнения с ней вычислений. Затем мне нужно скопировать результат в определенное место на хосте. Все решения, которые я нашел в Интернете, включают в себя сначала синхронизацию буфера, а затем его копирование в нужное мне место. Есть ли способ скопировать данные из 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];
}
hostBuff
— это общий буфер. Он приходит извне, поэтому я не могу быть уверен, что он был выделен mmap()
.@HamidYusifli, в моей текущей реализации - да. Если мне не нужен общий указатель на хосте для копирования данных из буфера, лучше использовать MTLResourceStorageModePrivate
.
Отвечая на ваш прямой вопрос: невозможно скопировать MTLBuffer
в указатель хоста. Я думаю, что причина сводится к тому, как виртуальная память отображается для процессора и графического процессора. И даже если бы он был, он был бы идентичен тому, как работают управляемые буферы, потому что команда, которую вы кодируете в кодировщике команд, выполняется на временной шкале графического процессора, что означает, что даже после вызова этого командного метода вы не увидите результат в указатель хоста, если вы не завершите кодирование в кодировщике команд, а затем commit
буфере команд, а затем дождитесь его завершения. Однако существует способ копирования между двумя MTLBuffer
: -[MTLBlitCommandEncoder copyFromBuffer:sourceOffset:toBuffer:destinationOffset:size:]
.
Но если вы используете управляемые буферы, есть другой способ сделать это. Вы можете просто вызвать -[MTLBlitCommandEncoder synchronizeResource:]
, чтобы сделать изменения, сделанные на временной шкале графического процессора, видимыми, и прочитать их содержимое через -[MTLBuffer contents]
.
Кроме того, если вы всегда считываете его обратно на ЦП, а объем данных относительно невелик, вы также можете использовать режим общего хранилища.
Есть две статьи, в которых более подробно рассматривается выбор режима хранения: Выбор режима хранения ресурсов в iOS и tvOS и Выбор режима хранения ресурсов в macOS .
Спасибо за ответ. Размер буфера в большинстве случаев составляет около 80 МБ, поэтому я не должен использовать здесь общий режим. Я не вижу проблем в ожидании завершения выполнения команды. Проблема, которую я пытаюсь решить, состоит в том, чтобы избежать избыточных операций копирования. В настоящее время я копирую из VRAM в RAM (синхронизирую) и копирую из одного места в RAM в другое (memcpy). Я не очень хочу memcpy
здесь, так как по логике должна быть возможность скопировать прямо в нужное мне место. AFAIK, это возможно с CUDA.
Ну, на самом деле я думаю, что вы можете использовать -[MTLDevice newBufferWithBytesNoCopy:length:options:deallocator:]
, чтобы «поделиться» частью виртуальной памяти, не копируя ее, но при этом использовать ее как «буфер хоста».
Будьте осторожны, читайте документацию, вы не можете поделиться какой-либо частью памяти, она должна быть выровнена по границе страницы и выделена чем-то вроде vm_allocate
Боюсь, я не могу использовать newBufferWithBytesNoCopy
здесь, потому что я продолжаю использовать буфер хоста после освобождения MTLBuffer.
Но подождите... deallocator
вызывается для какого буфера: RAM, VRAM или обоих? Они не упоминают об этом в документации: developer.apple.com/documentation/metal/mtldevice/…
Можно использовать версию без deallocator
или просто ничего не делать в блоке. Deallocator вызывается для указателя памяти, который вы передали конструктору, поэтому память хоста, когда MTLBuffer
выходит за пределы области видимости. Драйвер должен очистить VRAM за вас.
Нужно ли вам использовать опцию режима хранения
MTLResourceStorageModeManaged
для gpuBuff?