【发布时间】:2018-02-12 01:28:25
【问题描述】:
我目前有一个 MTLTexture 用于输入,并且正在使用一组 20-30 个顶点逐段渲染它。目前这是在MTKView 的drawRect 处理程序的尾部完成的:
[encoder setVertexBuffer:mBuff offset:0 atIndex:0]; // buffer of vertices
[encoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:1];
[encoder setFragmentTexture:inputTexture atIndex:0];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_vertexInfo.metalVertexCount];
[encoder endEncoding];
[commandBuffer presentDrawable:self.currentDrawable];
[commandBuffer commit];
但是,在进行最终的presentDrawable 之前,我想截取生成的纹理(我将把它的一个区域发送到单独的MTKView)。换句话说,我需要在drawPrimitives 调用之后访问某种形式的输出MTLTexture。
最有效的方法是什么?
一个想法是向中间输出MTLTexture 引入额外的drawPrimitives 渲染。我不知道该怎么做,但我会在这个过程中舀出那个输出纹理。我怀疑这甚至会在其他地方(即屏幕外)完成。
然后我会发出第二个drawPrimitives,使用具有该 outputTexture 的单个大型纹理四边形,然后在其上使用 presentDrawable。该代码将存在于我之前的代码所在的位置。
Metal API 中可能有一个简单的方法(我没有找到),它可以让我捕获drawPrimitives 的输出纹理。
我已经研究过使用MTLBlitCommandEncoder,但在某些 MacOSX 硬件上存在一些问题。
更新#1: idoogy,这是您要求的代码:
这里是我创建初始“亮度输出”纹理的地方...我们在顶点着色器中进行此操作:
...
[encoder setFragmentTexture:brightnessOutput atIndex:0];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_vertexInfo.metalVertexCount];
[encoder endEncoding];
for (AltMonitorMTKView *v in self.downstreamOutputs). // ancillary MTKViews
[v setInputTexture:brightnessOutput];
__block dispatch_semaphore_t block_sema = d.hostedAssetsSemaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
dispatch_semaphore_signal(block_sema);
}];
[commandBuffer presentDrawable:self.currentDrawable];
[commandBuffer commit];
下面,我们在辅助视图的drawRect 处理程序中,inputTexture 作为正在传输的纹理,显示它的一个子区域。我应该提到,这个MTKView 被配置为作为setNeedsDisplay 的结果而不是作为带有内部计时器的结果来绘制:
id<MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
encoder.label = @"Vertex Render Encoder";
[encoder setRenderPipelineState:metalVertexPipelineState];
// draw main content
NSUInteger vSize = _vertexInfo.metalVertexCount*sizeof(AAPLVertex);
id<MTLBuffer> mBuff = [self.device newBufferWithBytes:_vertexInfo.metalVertices
length:vSize
options:MTLResourceStorageModeShared];
[encoder setVertexBuffer:mBuff offset:0 atIndex:0];
[encoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:1];
[encoder setFragmentTexture:self.inputTexture atIndex:0];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_vertexInfo.metalVertexCount];
[encoder endEncoding];
[commandBuffer presentDrawable:self.currentDrawable];
[commandBuffer commit];
上面的代码似乎可以正常工作。话虽如此,我认为我们在 Xcode 调试器中讲述了一个不同的故事。很明显,我正在浪费大量时间以这种方式做事......那个长命令缓冲区是辅助监视器视图正在等待很多......
【问题讨论】:
-
呃……就这么简单吗(在endEncoding之后发出):id
currentTexture = [[self currentDrawable] texture]; -
而且,就我而言,我需要将 framebufferOnly 值设置为 false ——这样我就可以在其他地方重用输出。
-
明确地说,您是想仅仅读取生成的 MTLTexture 以在其他地方使用,还是希望在它出现之前对其进行修改?
-
我希望在备用 MTKView 中呈现生成的 MTLTexture 的子区域。
-
还想回答您上面关于仅获取当前可绘制对象的问题。问题在于时间——如果你在 endEncoding 之后调用它,那将永远不会起作用,因为 GPU 是完全异步的。在您调用 commit 之前,它甚至不会开始处理您的请求,因此纹理尚不包含输出。这就是你应该使用 waitUntilCompleted 或 addCompletionHandler() 的原因。