Изменение маленькой области моей текстуры приводит к тому, что остальная часть текстуры становится черной - PullRequest
0 голосов
/ 26 мая 2019

У меня есть приложение, которое выполняет некоторые вычисления на процессоре в нескольких потоках Каждый поток вычисляет область пикселей размером 64x64 для выходного изображения. Когда поток завершил свои вычисления, он загружает вычисленные данные пикселей в MTLTexture через -[MTLTexture replaceRegion:]. Перед запуском любого из потоков ЦП я поместил в текстуру версию полного результата в низком разрешении, и мне хотелось бы, чтобы потоки ЦП перезаписывали данные изображения при вычислении их с более высоким разрешением.

У меня это работает по большей части, но когда происходит первый вызов -replaceRegion:, кажется, что текстура очищается до черного, а затем заменяет область 64x64 по запросу.

Вот как я создаю текстуру:

MTLTextureDescriptor*   outputTextureDesc   = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
                                                                                             width:viewportSize.width
                                                                                            height:viewportSize.height
                                                                                         mipmapped:NO];
outputTextureDesc.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
_outputTexture = [device newTextureWithDescriptor:outputTextureDesc];

Затем, когда я хочу поместить в вывод текстуру в низком разрешении, я копирую ее из другой текстуры, подобной этой:

const Vertex2D quadVertices[] =
{
    //Pixel Positions, Texture Coordinates
    { { viewportSize.x / 2.0,  viewportSize.y / -2.0 }, { right, bottom } },
    { { viewportSize.x / -2.0, viewportSize.y / -2.0 }, { left, bottom } },
    { { viewportSize.x / -2.0, viewportSize.y / 2.0 }, { left, top } },

    { { viewportSize.x / 2.0,  viewportSize.y / -2.0 }, { right, bottom } },
    { { viewportSize.x / -2.0, viewportSize.y / 2.0 }, { left, top } },
    { { viewportSize.x / 2.0, viewportSize.y / 2.0 }, { right, top } },
};

MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments [ 0 ].texture = _outputTexture;
renderPassDescriptor.colorAttachments [ 0 ].loadAction = MTLLoadActionDontCare;
renderPassDescriptor.colorAttachments [ 0 ].storeAction = MTLStoreActionStore;

if(renderPassDescriptor != nil)
{
    id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
    commandBuffer.label = @"Copy Selection Command Buffer";

    // Create a render command encoder so we can render into something
    id<MTLRenderCommandEncoder> renderEncoder =
        [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
    renderEncoder.label = @"Copy Selection Command Encoder";

    [renderEncoder setViewport:(MTLViewport){0.0, 0.0, viewportSize.x, viewportSize.y, -1.0, 1.0 }];

    [renderEncoder setRenderPipelineState:renderPipelineState];

    [renderEncoder setVertexBytes:quadVertices
                           length:sizeof(quadVertices)
                          atIndex:MBV_VertexIndex];

    [renderEncoder setVertexBytes:&viewportSize
                           length:sizeof(viewportSize)
                          atIndex:MBV_ViewportSize];

    [renderEncoder setFragmentTexture:oldRender
                              atIndex:MBF_Texture];

    // Draw the vertices of our triangles
    [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
                      vertexStart:0
                      vertexCount:6];

    [renderEncoder endEncoding];
    [commandBuffer commit];
    [commandBuffer waitUntilCompleted];
}

Я также попытался изменить renderPassDescriptor.colorAttachment [ 0 ].loadAction на MTLLoadActionLoad вместо MTLLoadActionDontCare, как сказано в документации:

Существующее содержимое текстуры сохраняется.

но это не имело никакого значения.

Наконец, когда поток завершает работу, он загружает результат размером 64x64 пикселя, выполнив следующее:

MTLRegion currentRegion = { { colStart, rowStart, 0}, { colEnd - colStart, rowEnd - rowStart, 1 } };
[_outputTexture replaceRegion:currentRegion
                  mipmapLevel:0
                    withBytes:_outputBitmap + (rowStart * (int)viewportSize.width) + colStart
                  bytesPerRow:viewportSize.width * sizeof(*_outputBitmap)];

Если я использую инструменты отладки Metal, чтобы посмотреть на текстуру после начальной копии данных в низком разрешении, она содержит правильные пиксели. Но после первого звонка на -replaceRegion: все, кроме региона, заменяется черным Последующие вызовы -replaceRegion: работают правильно и не перезаписывают ранее записанные результаты.

Я должен отметить, что эта текстура также отображается, поскольку она обновляется MTKView. Я иногда вижу копию в низком разрешении для кадра или двух до того, как плитки с высоким разрешением начинают заполняться. Любая идея, почему вызов -replaceRegion: очистит текстуру? (Или что еще может очищать текстуру, если это не вызов -replaceRegion:?)

1 Ответ

2 голосов
/ 26 мая 2019

При условии, что это на macOS, а не на iOS, режим хранения текстуры по умолчанию равен MTLStorageModeManaged.Это означает, что вы должны явно синхронизировать текстуру, чтобы процессор "видел" любые изменения, сделанные графическим процессором.Так как ваше исходное изображение с низким разрешением рисуется в текстуре, это только на графическом процессоре, если / пока вы не синхронизируете.Поскольку вы не можете этого сделать, процессор имеет неинициализированные данные.Когда вы используете процессор для замены региона, он изменяет его (неинициализированную) копию, а затем передает его в графический процессор.Это заменяет нарисованный контент.

Для синхронизации вы должны создать кодировщик команды blit (MTLBlitCommandEncoder) и использовать его для кодирования команды синхронизации, используя -synchronizeResource: или (если вы хотите быть более избирательным) -synchronizeTexture:slice:level:.

Наконец, я не уверен, но я буду обеспокоен безопасностью потоков различных вызовов -replaceRegion:....Поэтому вы должны использовать очередь последовательной отправки или что-то еще для их сериализации.

...