我想创建一个使用金属的程序游戏,并且我正在使用基于八叉树的块方法来实现细节级别。金属块渲染
我正在使用的方法涉及CPU为地形创建八叉树节点,然后使用计算着色器在GPU上创建其网格。该网格存储在块对象中的顶点缓冲区和索引缓冲区中用于渲染。
所有这些似乎工作得很好,但是当涉及到渲染块时,我很早就遇到了性能问题。目前,我收集一系列块来绘制,然后将其提交给我的渲染器,这会创建一个MTLParallelRenderCommandEncoder
,然后为每个块创建一个MTLRenderCommandEncoder
,然后将其提交给GPU。
通过它的外观大约50%的CPU时间花费在为每个块创建MTLRenderCommandEncoder
上。目前,我只是为每个块创建一个简单的8顶点立方体网格,并且我有一个4x4x4的块大小,我在这些早期阶段下降到大约50fps。 (在现实中,它似乎只能有最多63 MTLRenderCommandEncoder
每个MTLParallelRenderCommandEncoder
,所以它不是完全型4x4x4)
我读过的MTLParallelRenderCommandEncoder
点是在一个单独的线程创建每个MTLRenderCommandEncoder
,但我没有太多的运气,让这个工作。此外,多线程不会绕过63个块的上限被渲染为最大值。
我觉得以某种方式将每个块的顶点和索引缓冲区合并为一个或两个较大的缓冲区以提交会有所帮助,但我不知道如何在没有大量调用的情况下如何执行此操作,以及这是否会改善效率。
这里是我的代码,需要的节点数组中,并提请他们:
func drawNodes(nodes: [OctreeNode], inView view: AHMetalView){
// For control of several rotating buffers
dispatch_semaphore_wait(displaySemaphore, DISPATCH_TIME_FOREVER)
makeDepthTexture()
updateUniformsForView(view, duration: view.frameDuration)
let commandBuffer = commandQueue.commandBuffer()
let optDrawable = layer.nextDrawable()
guard let drawable = optDrawable else{
return
}
let passDescriptor = MTLRenderPassDescriptor()
passDescriptor.colorAttachments[0].texture = drawable.texture
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.2, 0.2, 0.2, 1)
passDescriptor.colorAttachments[0].storeAction = .Store
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.depthAttachment.texture = depthTexture
passDescriptor.depthAttachment.clearDepth = 1
passDescriptor.depthAttachment.loadAction = .Clear
passDescriptor.depthAttachment.storeAction = .Store
let parallelRenderPass = commandBuffer.parallelRenderCommandEncoderWithDescriptor(passDescriptor)
// Currently 63 nodes as a maximum
for node in nodes{
// This line is taking up around 50% of the CPU time
let renderPass = parallelRenderPass.renderCommandEncoder()
renderPass.setRenderPipelineState(renderPipelineState)
renderPass.setDepthStencilState(depthStencilState)
renderPass.setFrontFacingWinding(.CounterClockwise)
renderPass.setCullMode(.Back)
let uniformBufferOffset = sizeof(AHUniforms) * uniformBufferIndex
renderPass.setVertexBuffer(node.vertexBuffer, offset: 0, atIndex: 0)
renderPass.setVertexBuffer(uniformBuffer, offset: uniformBufferOffset, atIndex: 1)
renderPass.setTriangleFillMode(.Lines)
renderPass.drawIndexedPrimitives(.Triangle, indexCount: AHMaxIndicesPerChunk, indexType: AHIndexType, indexBuffer: node.indexBuffer, indexBufferOffset: 0)
renderPass.endEncoding()
}
parallelRenderPass.endEncoding()
commandBuffer.presentDrawable(drawable)
commandBuffer.addCompletedHandler { (commandBuffer) -> Void in
self.uniformBufferIndex = (self.uniformBufferIndex + 1) % AHInFlightBufferCount
dispatch_semaphore_signal(self.displaySemaphore)
}
commandBuffer.commit()
}
这是一些很棒的建议,就像魅力一样。在16-18ms内CPU的帧数为3-4ms左右。非常感谢,我仍然在学习如何使用金属,并且我的大部分代码都是从Metal by Example中剥离出来的,它是基础教程。你真的帮了,谢谢。 –
另外,出于某种原因,我在我的脑海中有一个渲染命令编码器 - >一个绘制基元调用:S –
*“我的头脑中有一个渲染命令编码器 - >一个绘制原语调用”* ...这就是在幼稚使用OpenGL的情况下实际发生的情况 - 每次绘制调用都可能隐含地变成昂贵的GPU状态编译和上传。但金属使事情更加明确。如果看起来你是多余而低效的,你可能是。 :) – rickster