Как синхронизировать Metal Performance Shader с MTLBlitCommandEncoder?
Я пытаюсь лучше понять требования синхронизации при работе с Metal Performance Shaders
и MTLBlitCommandEncoder
,
у меня есть MTLCommandBuffer
это настроено следующим образом:
использование
MTLBlitCommandEncoder
скопировать область текстуры A в текстуру B. текстура A больше, чем текстура B. Я извлекаю "плитку" из текстуры A и копирую ее в текстуру B.Используйте
MPSImageBilinearScale
металлический шейдер производительности с текстурой B в качестве исходной текстуры и третьей текстурой, Texture C, в качестве места назначения. Этот металлический шейдер производительности масштабирует и потенциально переводит содержимое текстуры B в текстуру C.
Как мне убедиться, что blit-кодировщик полностью заканчивает копирование данных из текстуры A в текстуру B, прежде чем металлический шейдер производительности начнет пытаться масштабировать текстуру B? Должен ли я вообще беспокоиться об этом, или последовательный характер буфера команд уже позаботился об этом?
Металл имеет концепцию использования заборов MTLFence
для синхронизации доступа к ресурсам, но я все равно не вижу, чтобы металлический шейдер производительности ждал на заборе. (В то время как waitForFence:
присутствует на энкодерах.)
Если я не могу использовать заборы и мне нужно синхронизировать, рекомендуется просто поставить в очередь blit encoder, а затем вызвать waitUntilCompleted
в буфере команд, прежде чем ставить в очередь шейдер и вызывать waitUntilCompleted
второй раз? например:
id<MTLCommandBuffer> commandBuffer;
// Enqueue blit encoder to copy Texture A -> Texture B
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
[blitEncoder copyFromTexture:...];
[blitEncoder endEncoding];
// Wait for blit encoder to complete.
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
// Scale Texture B -> Texture C
MPSImageBilinearScale *imageScaleShader = [[MPSImageBilinearScale alloc] initWithDevice:...];
[imageScaleShader encodeToCommandBuffer:commandBuffer...];
// Wait for scaling shader to complete.
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
Причина, по которой я думаю, что мне нужно сделать промежуточное копирование в текстуру B, заключается в том, что MPSImageBilinearScale
кажется, масштабировать всю исходную текстуру. clipOffset
полезен для вывода, но не применяется к реальному масштабированию или преобразованию. Таким образом, плитка должна быть извлечена из текстуры A в текстуру B того же размера, что и сама плитка. Тогда масштабирование и преобразование будут "иметь смысл". Не обращайте внимания на эту сноску, потому что я забыл некоторые основные математические принципы и с тех пор выяснил, как заставить свойства преобразования масштабного преобразования работать с clipRect.
1 ответ
Металл позаботится об этом за тебя. Драйвер и графический процессор выполняют команды в буфере команд, как если бы они были последовательными. ("Как будто" позволяет запускать вещи параллельно или не по порядку для эффективности, но только если результат будет таким же, как при последовательном выполнении.)
Проблемы с синхронизацией возникают, когда и CPU, и GPU работают с одними и теми же объектами. Также с представлением текстур на экране. (Вы не должны рендерить текстуру, которая представлена на экране.)
В Руководстве по программированию Metal есть раздел, посвященный доступу чтения и записи к ресурсам с помощью шейдеров, который не совсем совпадает, но должен вас успокоить:
Барьеры памяти
Между командными кодерами
Все записи ресурсов, выполненные в данном кодере команд, отображаются в следующем кодере команд. Это верно как для кодировщиков команд рендеринга, так и для вычислений.
В кодере команд Render
Для буферов атомарные записи видны для последующих атомарных чтений в нескольких потоках.
Для текстур
textureBarrier
метод гарантирует, что записи, выполненные в данном вызове отрисовки, видны для последующих операций чтения в следующем вызове отрисовки.В кодировщике вычислительных команд
Все записи ресурсов, выполняемые в данной функции ядра, видны в следующей функции ядра.
MPS находится поверх Metal (в основном). Он не заменяет его (в основном). Вы можете предположить, что он использует обычные кодировщики команд, которые вы используете.
Есть несколько областей, где требуются MTLFences, особенно при взаимодействии с кодировщиками рендеринга и MTLHeaps. Когда они доступны, используйте методы синхронизации для MPSImages и типов буферов, а не накручивайте свои собственные.