Очень неуловимая проблема металла с 'nextDrawable' - PullRequest
0 голосов
/ 21 октября 2018

У меня есть четыре экземпляра 1920x1080 MTKView в качестве подпредставлений NSWindow на мониторе 3840x2160 в качестве конфигурации отладки.Представьте, что мои четыре вида пронумерованы как квадранты («1» в правом верхнем углу, затем против часовой стрелки).На этих просмотрах играют четыре видео.Их CVPixelBuffers снимаются с каждого соответствующего AVPlayer через выделенный CVDisplayLink цикл рендеринга для каждого.Каждый MTKView настроен для рисования вручную с помощью тех же четырех обратных вызовов CVDisplayLink.Вызов MTKView draw находится внутри @autoreleasepool.

Все это работает довольно хорошо примерно в 95% случаев - при использовании около 75% памяти GPU и около 20% обработки GPU наRadeon R9 M395, iMac 5k, конец 2015 года, с Fusion Drive.В Metal Trace в XCode все мои графические процессы отображаются не более 5 мсек на кадр.

Я пытаюсь выяснить, почему некоторые мои видео заикаются примерно в 5% случаев. Захват GPU-кадра дает мне подсказку, но я не уверен, что с этим делать.

В каждом вызове MTKView drawRect я выполняю 4 прохода работы шейдера ядра над текстурой, котораяЯ выделил и до двух входящих CVPixelBuffer текстур (т. Е. Для перехода от одного видео к другому).Я не запрашиваю и не трогаю currentRenderPassDescriptor там.

Сразу после этих партий MTLComputeCommandEncoder работы у меня есть один последний проход с MTLRenderCommandEncoder и набор вершин с окончательной текстурой вычислениярезультат ввода:

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    dispatch_semaphore_wait(self.inflightSemaphore, DISPATCH_TIME_FOREVER);
    id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];

    <do 4 passes of compute work>

    // ********* The next line is where WARNINGS (see below) occur ***********
    MTLRenderPassDescriptor *renderPassDescriptor = self.currentRenderPassDescriptor;

    id<MTLDrawable> currentDrawable = nil;
    if (renderPassDescriptor) {
       currentDrawable = self.currentDrawable;
       [self doVertexWork:inputTexture
          onCommandBuffer:commandBuffer
           withDescriptor:renderPassDescriptor];
    }

    [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
        dispatch_semaphore_signal(self.inflightSemaphore);
    }];

    if (currentDrawable)
        [commandBuffer presentDrawable:currentDrawable];
    [commandBuffer commit];

    commandBuffer=nil;
}


Если я делаю захват кадров GPU, я отмечаю частоту кадров 120 кадров в секунду, 4 вызова отрисовки, 4 буфера команд и 4 кодировщика рендеринга.Однако в указанной строке я получаю 7 предупреждений:

  • CAMetalLayer nextDrawable Called Early (один раз)
  • CAMetalLayer nextDrawable was Called Unnecessarily (три раза)
  • Command Buffer was not Enqueued and Committed in this Frame (три раза - команда буферизует на квадрантах 1,3,4)

Кстати, примечание о раннем вызове, предоставленное Xcode: "Your application called CAMetalLayer nextDrawable earlier than needed. This may block execution until a CAMetalDrawable is available. Delay the call to nextDrawable until the drawing is needed for encoding".

Я замечаю вДокументы о том, что заблокированное выполнение происходит в течение одной секунды, что соответствует времени заикания, которое я периодически вижу (т.е. обновляется каждые 1 секунду или около того).

При вызове endEncoding, когда я делаюВ моей работе с вершинами я получаю предупреждение "CAMetalDrawable used from Earlier Frame" (три раза, с текстурами только из квадрантов 1,2,4).

Интересное примечание: когда при захвате кадра паузы останавливались, изображение из квадранта 3 былопродублирована в квадранте 2, а изображение квадранта два не было видно.

Я не могу понять, в чем моя проблема, поскольку я следую методам, использованным во всех примерах, которые я виделтам.

Любая помощь будет оценена.


UPДАТА # 1

Основываясь на Кен-Томасе полезных комментариях, я вставил следующую строку сразу после создания буфера команд в вызове отрисовки:

[metalCommandQueue insertDebugCaptureBoundary];

Это решило почти все предполагаемые проблемы, но я все еще время от времени наблюдаю 1-секундное заикание.Единственная оставшаяся проблема - это согласованный CAMetalLayer nextDrawable was Called Unnecessarily с комментарием, что «ваше приложение вызвало CAMetalLayer nextDrawable, но не использовало возвращенное CAMetalDrawable во время фрейма».Это происходит независимо от того, происходит ли заикание.

Насколько я могу судить, этот nextDrawable звонок иногда останавливает меня.


...