![](/img/trans.png)
[英]Metal: using multiple MTLRenderCommandEncoder in one rendering pass
[英]Device Load errors when rendering an ARMeshGeometry using Metal
我正在使用 ARKit 進行場景重建,需要用金屬渲染捕獲的場景幾何體。 我可以通過ARMeshAnchor.geometry
訪問這個幾何圖形,它是一個ARMeshGeometry
。 但是,當我嘗試使用我的自定義金屬渲染管道渲染它時,沒有任何渲染,並且我收到一堆這樣的錯誤:
Invalid device load executing vertex function "myVertex" encoder: "0", draw: 3, at offset 4688
這是我一直用於調試的代碼的高度簡化版本:
struct InOut {
float4 position [[position]];
};
vertex InOut myVertex(
uint vid [[vertex_id]],
const constant float3* vertexArray [[buffer(0)]])
{
TouchInOut out;
const float3 in = vertexArray[vid];
out.position = float4(in.position, 1);
}
fragment float4 myFragment(TouchInOut in [[stage_in]]){
return float4(1, 0, 0, 1);
}
// Setup MTLRenderPipelineDescriptor
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.colorAttachments[0].pixelFormat = .rgba8Unorm
pipelineDescriptor.sampleCount = 1
pipelineDescriptor.vertexFunction = defaultLibrary.makeFunction(name: "myVertex")
pipelineDescriptor.fragmentFunction = defaultLibrary.makeFunction(name: "myFragment")
let vertexDescriptor = MTLVertexDescriptor()
vertexDescriptor.attributes[0].format = .float3
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.layouts[0].stride = MemoryLayout<SIMD3<Float>>.stride
pipelineDescriptor.vertexDescriptor = vertexDescriptor
func render(arMesh: ARMeshAnchor) -> void {
// snip... — Setting up command buffers
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
renderEncoder.setViewport(MTLViewport(originX: 0, originY: 0, width: 512, height: 512, znear: 0, zfar: 1))
renderEncoder.setRenderPipelineState(renderPipelineState)
let vertices = arMesh.geometry.vertices
let faces = arMesh.geometry.faces
renderEncoder.setVertexBuffer(vertices.buffer, offset: 0, index: 0)
renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: faces.count * 3, indexType: .uint32, indexBuffer: buffer, indexBufferOffset: 0)
renderEncoder.endEncoding()
// snip... — Clean up
}
我不明白為什么這段代碼會導致金屬異常。 如果我將着色器中的vid
限制在 100 左右,它就會停止拋出,但它仍然不能正確繪制任何東西
這里發生了什么? 為什么我的代碼會產生錯誤,我該如何解決?
這里的問題是頂點數據的對齊/打包。
ARMeshGeometry.vertices
中的每個頂點由 3 個浮點組件組成,總大小為 12 個字節。 上面的代碼假設這意味着數據是一個float3
/ SIMD3<Float>
,但是來自ARMeshGeometry
的vertices
實際上是緊密打包的。 因此,雖然SIMD3<Float>
的步幅為16
,但實際頂點數據的步幅為12
。
float3
(16) 與vertices
緩沖區 (12) 中元素的實際大小相比較大,導致金屬嘗試訪問vertices
緩沖區末尾的數據,從而產生錯誤。
這里有兩個重要的修復:
MTLVertexDescriptor
具有正確的步幅:let exampleMeshGeometry: ARMeshGeometry = ...
vertexDescriptor.layouts[0].stride = exampleMeshGeometry.vertices.stride
packed_float3
而不是float3
vertex InOut myVertex(
uint vid [[vertex_id]],
const constant packed_float3* vertexArray [[buffer(0)]])
{
...
}
修復這些問題后,您應該能夠將ARMeshGeometry
緩沖區正確傳輸到金屬着色器
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.