簡體   English   中英

使用 Metal 渲染 ARMeshGeometry 時出現設備加載錯誤

[英]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> ,但是來自ARMeshGeometryvertices實際上是緊密打包的。 因此,雖然SIMD3<Float>的步幅為16 ,但實際頂點數據的步幅為12

float3 (16) 與vertices緩沖區 (12) 中元素的實際大小相比較大,導致金屬嘗試訪問vertices緩沖區末尾的數據,從而產生錯誤。

這里有兩個重要的修復:

  1. 確保MTLVertexDescriptor具有正確的步幅:
let exampleMeshGeometry: ARMeshGeometry = ...

vertexDescriptor.layouts[0].stride = exampleMeshGeometry.vertices.stride
  1. 在着色器中,使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM