【发布时间】:2019-05-26 05:56:02
【问题描述】:
我有一个应用程序在多个线程上对 CPU 进行一些计算。每个线程计算输出图像的 64x64 像素区域。当一个线程完成计算后,它会将计算得到的像素数据通过-[MTLTexture replaceRegion:]上传到MTLTexture。在任何 CPU 线程启动之前,我已将完整结果的低分辨率版本放入纹理中,我希望 CPU 上的线程在以更高分辨率计算图像数据时覆盖图像数据。
我大部分时间都在工作,但是当第一次调用 -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:的调用,还有什么可能会清除纹理?)
【问题讨论】: