I want to render 2 different objects with Metal...I have 2 different shaders, different renderpipeline, and command buffers, passDescriptors, they are all different..But there is only one object is drawing on the screen, I don't know where i went wrong.... Here is the draw function:
dispatch_semaphore_wait(inflightSemaphore, DISPATCH_TIME_FOREVER)
//Sky
if let drawable = metalLayer.nextDrawable()
{
var modelMatrixTransSky = M4f()
var modelMatrixRotSky = M4f()
var modelMatrixScaleSky = M4f()
modelMatrixTransSky = translate(0, y: 0, z: 0)
modelMatrixRotSky = rotate(90, r: V3f(1,0,0)) * modelMatrixRotSky
modelMatrixScaleSky = scaling(10, y: 10, z: 10)
let modelMatrixSky = modelMatrixTransSky * modelMatrixRotSky * modelMatrixScaleSky
var viewMatrixSky = M4f()
viewMatrixSky = myCamera.setLookAt(viewMatrixSky)
let modelViewMatrixSky = viewMatrixSky * modelMatrixSky
let aspect = Float32(metalLayer.drawableSize.width) / Float32(metalLayer.drawableSize.height)
let kFOVY:Float = 85.0
let projectionMatrix = perspective_fov(kFOVY, aspect: aspect, near: 0.1, far: 180.0)
let matricesSky = [projectionMatrix, modelViewMatrixSky]
memcpy(uniformBufferSky.contents(), matricesSky, Int(sizeof(M4f) * 2))
let commandBufferSky = commandQueue.commandBuffer()
commandBufferSky.addCompletedHandler{ [weak self] commandBufferSky in
if let strongSelf = self {
dispatch_semaphore_signal(strongSelf.inflightSemaphore)
}
return
}
//Model
var modelMatrixTransModel = M4f()
var modelMatrixRotModel = M4f()
var modelMatrixScaleModel = M4f()
modelMatrixTransModel = translate(0, y: 0, z: 0)
modelMatrixRotModel = rotate(0, r: V3f(1,0,0))
modelMatrixScaleModel = scaling(10, y: 10, z: 10)
let modelMatrixModel = modelMatrixTransModel * modelMatrixRotModel * modelMatrixScaleModel
var viewMatrixModel = M4f()
viewMatrixModel = myCamera.setLookAt(viewMatrixModel)
let modelViewMatrixModel = viewMatrixModel * modelMatrixModel
let matricesModel = [projectionMatrix, modelViewMatrixModel]
memcpy(uniformBufferModel.contents(), matricesModel, Int(sizeof(M4f) * 2))
let commandBufferModel = commandQueue.commandBuffer()
commandBufferModel.addCompletedHandler{ [weak self] commandBufferModel in
if let strongSelf = self {
dispatch_semaphore_signal(strongSelf.inflightSemaphore)
}
return
}
//Sky
let passDescriptorSky = MTLRenderPassDescriptor()
passDescriptorSky.colorAttachments[0].texture = drawable.texture
passDescriptorSky.colorAttachments[0].clearColor = MTLClearColorMake(0.05, 0.05, 0.05, 1)
passDescriptorSky.colorAttachments[0].loadAction = .Clear
passDescriptorSky.colorAttachments[0].storeAction = .Store
passDescriptorSky.depthAttachment.texture = depthTextureSky
passDescriptorSky.depthAttachment.clearDepth = 1
passDescriptorSky.depthAttachment.loadAction = .Clear
passDescriptorSky.depthAttachment.storeAction = .DontCare
//Model
let passDescriptorModel = MTLRenderPassDescriptor()
passDescriptorModel.colorAttachments[0].texture = drawable.texture
passDescriptorModel.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptorModel.colorAttachments[0].loadAction = .Clear
passDescriptorModel.colorAttachments[0].storeAction = .Store
//Sky
let commandEncoderSky = commandBufferSky.renderCommandEncoderWithDescriptor(passDescriptorSky)
let commandEncoderModel = commandBufferModel.renderCommandEncoderWithDescriptor(passDescriptorModel)
commandEncoderSky.setRenderPipelineState(pipelineSky)
commandEncoderSky.setDepthStencilState(depthStencilState)
commandEncoderSky.setFrontFacingWinding(.CounterClockwise)
commandEncoderSky.setCullMode(.Back)
commandEncoderSky.setVertexBuffer(vertexBufferSky, offset:0, atIndex:0)
commandEncoderSky.setVertexBuffer(uniformBufferSky, offset:0, atIndex:1)
commandEncoderSky.setFragmentTexture(diffuseTextureSky, atIndex: 0)
commandEncoderSky.setFragmentSamplerState(samplerStateSky, atIndex: 0)
commandEncoderSky.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: vertexCountSky)
commandEncoderSky.endEncoding()
commandBufferSky.presentDrawable(drawable)
//Model
commandEncoderModel.setRenderPipelineState(pipelineModel)
commandEncoderModel.setDepthStencilState(depthStencilState)
commandEncoderModel.setFrontFacingWinding(.CounterClockwise)
commandEncoderModel.setCullMode(.Back)
commandEncoderModel.setVertexBuffer(vertexBufferModel, offset:0, atIndex:0)
commandEncoderModel.setVertexBuffer(normalBufferModel, offset:0, atIndex:1)
commandEncoderModel.setVertexBuffer(colorBufferModel, offset:0, atIndex:2)
commandEncoderModel.setVertexBuffer(uniformBufferModel, offset:0, atIndex:3)
commandEncoderModel.setFragmentBuffer(uniformBufferModel, offset: 0, atIndex: 0)
commandEncoderModel.drawPrimitives(.Point, vertexStart: 0, vertexCount: vertextCountModel)
commandEncoderModel.endEncoding()
commandBufferModel.presentDrawable(drawable)
// bufferIndex matches the current semaphore controled frame index to ensure writing occurs at the correct region in the vertex buffer
bufferIndex = (bufferIndex + 1) % MaxBuffers
commandBufferSky.commit()
bufferIndex = (bufferIndex + 1) % MaxBuffers
commandBufferModel.commit()
}
and here is the function for building the pipeline:
//Model
let library = device!.newDefaultLibrary()!
let vertexFunctionModel = library.newFunctionWithName("vertex_ply")
let fragmentFunctionModel = library.newFunctionWithName("fragment_ply")
let vertexFunctionSky = library.newFunctionWithName("vertex_sky")
let fragmentFunctionSky = library.newFunctionWithName("fragment_sky")
//Model
let vertexDescriptorModel = MTLVertexDescriptor()
vertexDescriptorModel.attributes[0].offset = 0
vertexDescriptorModel.attributes[0].format = .Float4
vertexDescriptorModel.attributes[0].bufferIndex = 0
vertexDescriptorModel.layouts[0].stepFunction = .PerVertex
vertexDescriptorModel.layouts[0].stride = sizeof(Float) * 4
//Sky
let vertexDescriptorSky = MTLVertexDescriptor()
vertexDescriptorSky.attributes[0].offset = 0
vertexDescriptorSky.attributes[0].format = .Float4
vertexDescriptorSky.attributes[0].bufferIndex = 0
vertexDescriptorSky.attributes[1].offset = sizeof(Float32) * 4
vertexDescriptorSky.attributes[1].format = .Float4
vertexDescriptorSky.attributes[1].bufferIndex = 0
vertexDescriptorSky.attributes[2].offset = sizeof(Float32) * 8
vertexDescriptorSky.attributes[2].format = .Float2
vertexDescriptorSky.attributes[2].bufferIndex = 0
vertexDescriptorSky.layouts[0].stepFunction = .PerVertex
vertexDescriptorSky.layouts[0].stride = sizeof(Vertex)
//Model
let pipelineDescriptorModel = MTLRenderPipelineDescriptor()
pipelineDescriptorModel.vertexFunction = vertexFunctionModel
pipelineDescriptorModel.vertexDescriptor = vertexDescriptorModel
pipelineDescriptorModel.fragmentFunction = fragmentFunctionModel
pipelineDescriptorModel.colorAttachments[0].pixelFormat = .BGRA8Unorm
//Sky
let pipelineDescriptorSky = MTLRenderPipelineDescriptor()
pipelineDescriptorSky.vertexFunction = vertexFunctionSky
pipelineDescriptorSky.vertexDescriptor = vertexDescriptorSky
pipelineDescriptorSky.fragmentFunction = fragmentFunctionSky
pipelineDescriptorSky.colorAttachments[0].pixelFormat = .BGRA8Unorm
pipelineDescriptorSky.depthAttachmentPixelFormat = .Depth32Float
//Model
do {
pipelineModel = try device!.newRenderPipelineStateWithDescriptor(pipelineDescriptorModel)
} catch {
print("error with device.newRenderPipelineStateWithDescriptor")
}
// //Sky
do {
pipelineSky = try device!.newRenderPipelineStateWithDescriptor(pipelineDescriptorSky)
} catch {
print("error with device.newRenderPipelineStateWithDescriptor")
}
//Model
let depthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilDescriptor.depthCompareFunction = .Less
depthStencilDescriptor.depthWriteEnabled = true
depthStencilState = device!.newDepthStencilStateWithDescriptor(depthStencilDescriptor)
commandQueue = device!.newCommandQueue()
//Sky
let samplerDescriptorSky = MTLSamplerDescriptor()
samplerDescriptorSky.minFilter = .Nearest
samplerDescriptorSky.magFilter = .Linear
samplerStateSky = device!.newSamplerStateWithDescriptor(samplerDescriptorSky)
Unless you want to do encoding work across threads, you only need to create one command buffer per frame. Also, unless you need to perform rendering in multiple passes (not just multiple draw calls, actually reading image data back from one pass in a subsequent pass), you only need one render command encoder per command buffer.
In pseudocode, here's what one frame looks like:
// semaphore wait
commandBuffer = commandQueue.makeCommandBuffer()!
commandBuffer.addCompletedHandler {
// semaphore signal
}
commandEncoder = commandBuffer.makeRenderCommandEncoder()!
for obj in objects {
commandEncoder.setRenderPipelineState(renderPipeline)
commandEncoder.setVertexBuffer(...)
// set other per-draw state on the encoder
commandEncoder.drawPrimitives(...)
}
commandEncoder.endEncoding()
commandBuffer.presentDrawable(currentDrawable)
commandBuffer.commit()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With