Tôi đang cố gắng tạo một trò chơi thủ tục sử dụng Metal, và tôi đang sử dụng một phương pháp tiếp cận dựa trên octree để thực hiện Cấp độ chi tiết.Chunk Rendering in Metal
Phương pháp tôi đang sử dụng liên quan đến CPU tạo các nút octree cho địa hình, sau đó lưới của nó được tạo trên GPU bằng cách sử dụng trình đổ bóng tính toán. Lưới này được lưu trữ trong một bộ đệm đỉnh và bộ đệm chỉ mục trong đối tượng chunk để render.
Tất cả điều này dường như hoạt động khá tốt, tuy nhiên khi nói đến các khối hiển thị, tôi sẽ gặp vấn đề về hiệu suất từ rất sớm. Hiện tại tôi thu thập một mảng khối để vẽ, sau đó gửi cho trình kết xuất của tôi, điều đó sẽ tạo ra một MTLParallelRenderCommandEncoder
để sau đó tạo một MTLRenderCommandEncoder
cho mỗi đoạn, sau đó được gửi tới GPU.
Bằng vẻ bề ngoài, khoảng 50% thời gian CPU được dùng để tạo MTLRenderCommandEncoder
cho mỗi đoạn. Hiện tại tôi chỉ đang tạo ra một khối lập phương 8 đỉnh đơn giản cho mỗi đoạn, và tôi có một mảng 4x4x4 và tôi giảm xuống khoảng 50fps trong những giai đoạn đầu. (Trên thực tế có vẻ như chỉ có có thể lên đến 63 MTLRenderCommandEncoder
trong mỗi MTLParallelRenderCommandEncoder
do đó, nó không hoàn toàn tạo của cầu 4x4x4)
Tôi đã đọc rằng điểm của MTLParallelRenderCommandEncoder
là để tạo ra từng MTLRenderCommandEncoder
trong một thread riêng biệt, nhưng tôi đã không có nhiều may mắn với việc này để làm việc. Ngoài ra đa luồng nó sẽ không nhận được xung quanh nắp của 63 khối được trả lại như là một tối đa.
Tôi cảm thấy rằng bằng cách nào đó hợp nhất các bộ đệm đỉnh và chỉ mục cho từng đoạn thành một hoặc hai bộ đệm lớn hơn để gửi sẽ giúp ích, nhưng tôi không chắc chắn làm thế nào để thực hiện điều này mà không cần gọi số memcpy()
. hiệu quả.
Dưới đây là mã của tôi mà mất trong mảng các nút và lôi kéo họ:
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()
}
Đó là một số lời khuyên tuyệt vời, hoạt động như một sự quyến rũ. Đã đi từ 16-18ms cho một khung trên CPU đến khoảng 3-4ms. Cảm ơn rất nhiều, tôi vẫn đang học cách sử dụng kim loại và hầu hết mã của tôi đã bị loại bỏ khỏi Metal bằng ví dụ và đó là hướng dẫn cơ bản. Bạn đã thực sự giúp đỡ, cảm ơn bạn. –
Ngoài ra, vì một lý do nào đó tôi đã có nó trong đầu của tôi là một bộ mã hóa lệnh -> một mồi gọi nguyên thủy: S –
* "Tôi đã có nó trong đầu của tôi là một bộ mã hóa lệnh -> một mồi gọi" * ... đó là những gì hiệu quả xảy ra với việc sử dụng OpenGL ngây thơ - mỗi cuộc gọi vẽ có thể biến thành một bản dịch đắt tiền và tải lên trạng thái GPU. Nhưng Metal làm cho mọi thứ rõ ràng hơn. Nếu có vẻ như bạn đang thừa kế và không hiệu quả, có thể bạn đang có. :) – rickster