METAL顶点/片段着色器“拦截”结果的最佳方法
Posted
技术标签:
【中文标题】METAL顶点/片段着色器“拦截”结果的最佳方法【英文标题】:Best way to "intercept" result of METAL vertex/fragment shader 【发布时间】: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这应该是可行的。在您对commandBuffer
调用commit
之前,通过调用[commandBuffer addCompletedHandler:]
添加命令缓冲区的完成处理程序,然后在完成处理程序中,从renderPassDescriptor
获取颜色附件。
renderPassDescriptor
保存当前正在绘制的附件集,并由MTKView
自动配置。实际纹理每帧旋转,因为MTKView
正在使用三重缓冲来确保 GPU 的持续利用,但只要您在完成处理程序中,该特定附件就不会被释放以供未来帧使用,这样您就可以安全地从中读取、复制等。
注意:确保以合理的速度完成您的处理程序,否则您的帧速率将会下降(因为MTKView
将很快用完渲染目标,并且会一直呆在那里直到它们被释放)。
这里有一个通用代码 sn-p 帮助您入门:
// Grab the current render pass descriptor from MTKView so it's accessible from within the completion block:
__block MTLRenderPassDescriptor *renderPassDescriptor = self.renderPassDescriptor;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> commandBuffer)
// This will be called once the GPU has completed rendering your frame.
// This is your output texture:
id <MTLTexture> outputTexture = renderPassDescriptor.colorAttachments[0].texture;
];
[commandBuffer commit];
【讨论】:
这是很好的信息。当我想在单个命令缓冲区中使用多个混合的内核/顶点渲染阶段时,我对您的解释感到困惑——其中一个阶段的纹理输出提供下一个阶段。您的回答似乎表明需要多个命令缓冲区和“等待”。有趣的是,这不是我目前所经历的。 嗯,这取决于您如何使用某个绘图调用的输出。如果您在同一命令缓冲区中的另一个命令中使用它,那就没问题了。如果您在其他地方使用它,则必须等待命令缓冲区完成。 我读到通常每帧有一个命令缓冲区,具有多个渲染通道描述符(每个“通道”一个 fir)。我猜你使用MTKView
创建“中间”rpds 并使用self.renderPassDescriptor
作为最终渲染到屏幕?
在这种情况下,passes 的序列化应该由每个 rpd 创建的命令编码器强制执行...?我仍在解码 Apple 的“ObjectsExample”项目...以上是关于METAL顶点/片段着色器“拦截”结果的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
使用金属顶点和片段着色器将 MTLTexture 传递给 SCNProgram