2015-11-05 26 views
5

Metal bir gölgelendirici içine beslenen bazı konumların hesaplanması için bir Metal hesaplama gölgelendiricisi kullanmak istiyorum. Düz ileriye doğru geliyor, ancak MTLBuffer veriyi Metal tabanlı SCNProgram'a almakta sorun yaşıyorum.Metal tamponun SceneKit gölgelendiricisine geçilmesi

İşlem çekirdeği şu şekildedir, bu örnekte üç 3B vektörü (her iki arabellekte) çekmektedir.

kernel void doSimple(const device float3 *inVector [[ buffer(0) ]], 
        device float3 *outVector [[ buffer(1) ]], 
        uint id [[ thread_position_in_grid ]]) { 

    float yDisplacement = 0; 
    . . . . 

    outVector[id] = float3(
     inVector[id].x, 
     inVector[id].y + yDisplacement, 
     inVector[id].z); 
} 

Bu çekirdek fonksiyonu zaman SCNSceneRendererDelegate arasında - renderer:willRenderScene:atTime: yönteminde her kare çalıştırılır. İki arabellek var ve her kareden sonra değiştiriliyorlar.

Arabellekler şu şekilde oluşturulur;

func setupBuffers() { 
    positions = [vector_float3(0,0,0), vector_float3(1,0,0), vector_float3(2,0,0)] 

    let bufferSize = sizeof(vector_float3) * positions.count 
    //copy same data into two different buffers for initialisation 
    buffer1 = device.newBufferWithBytes(&positions, length: bufferSize, options: .OptionCPUCacheModeDefault) 
    buffer2 = device.newBufferWithBytes(&positions, length: bufferSize, options: .OptionCPUCacheModeDefault) 
} 

ve işlem gölgelendirici kullanılarak çalıştırılır (willRenderScene fonksiyon olarak), aşağıdaki;

let computeCommandBuffer = commandQueue.commandBuffer() 
    let computeCommandEncoder = computeCommandBuffer.computeCommandEncoder() 

    computeCommandEncoder.setComputePipelineState(pipelineState) 
    computeCommandEncoder.setBuffer(buffer1, offset: 0, atIndex: 0) 
    computeCommandEncoder.setBuffer(buffer2, offset: 0, atIndex: 1) 

    computeCommandEncoder.dispatchThreadgroups(numThreadgroups, threadsPerThreadgroup: threadsPerGroup) 
    computeCommandEncoder.endEncoding() 
    computeCommandBuffer.commit() 
    computeCommandBuffer.waitUntilCompleted() 

    let bufferSize = positions.count*sizeof(vector_float3) 
    var data = NSData(bytesNoCopy: buffer2.contents(), length: bufferSize, freeWhenDone: false) 
    var resultArray = [vector_float3](count: positions.count, repeatedValue: vector_float3(0,0,0)) 
    data.getBytes(&resultArray, length:bufferSize) 

    for outPos in resultArray { 
     print(outPos.x, ", ", outPos.y, ", ", outPos.z) 
    } 

Bu çalışır ve benim shader miktarı y dizideki her vektörü için koordinat güncelliyor görebilirsiniz.

Bu sahne eşit aralıklı üç küreden oluşur. Köşe gölgelendirici, hesaplama gölgelendiricisinde hesaplanan pozisyonu alır ve her bir köşe konumuna ekler (yine de y bileşenini). Her bir küreye bir indeks verdim, vertex shader, hesaplanmış dizilimden uygun konumu çekmek için bu dizini kullanır.

Metal köşe işlevi aşağıda gösterilmiştir, bir SCNProgram tarafından başvurulan ve her bir kürenin malzemesine ayarlanmıştır.

vertex SimpleVertex simpleVertex(SimpleVertexInput in [[ stage_in ]], 
          constant SCNSceneBuffer& scn_frame [[buffer(0)]], 
          constant MyNodeBuffer& scn_node [[buffer(1)]], 
          constant MyPositions &myPos [[buffer(2)]], 
          constant uint &index [[buffer(3)]] 
          ) 
{ 

    SimpleVertex vert; 
    float3 posOffset = myPos.positions[index]; 
    float3 pos = float3(in.position.x, 
         in.position.y + posOffset.y, 
         in.position.z); 

    vert.position = scn_node.modelViewProjectionTransform * float4(pos,1.0); 

    return vert; 
} 

MyPositions float3s bir dizi ihtiva eden basit bir yapıdır.

struct MyPositions 
{ 
    float3 positions[3]; 
}; 

(Ayrıca willRenderScene yöntemde yapılır), aşağıda gösterildiği gibi, her küre malzeme setValue yöntemi kullanılarak tepe gölgelendiriciye veri geçen bir sorun vardır. Herşey beklendiği gibi çalışır (üç küre yukarı doğru hareket eder).

var i0:UInt32 = 0 
    let index0 = NSData(bytes: &i0, length: sizeof(UInt32)) 
    sphere1Mat.setValue(index0, forKey: "index") 
    sphere1Mat.setValue(data, forKey: "myPos") 

AMA bu veri GPU'ya CPU'ya GPU kopyalanan ve gerçekten ben çok önleyeceğini şeydir olması gerekir. Yani benim sorum ... Bir MTLBuffer'ı bir SCNProgram'a nasıl geçirebilirim?

willRenderScene aşağıdaki çalıştı ama EXEC_BAD...

let renderCommandEncoder = renderer.currentRenderCommandEncoder! 
renderCommandEncoder.setVertexBuffer(buffer2, offset: 0, atIndex: 2) 
renderCommandEncoder.endEncoding() 

Komple example is over on GitHub ama hiçbir şey elde ettiniz.

Okuma için teşekkürler, bununla mücadele ediyorum. Geçici çözüm, bir MTLBuffer yerine bir MTLTexture kullanmaktır, çünkü bunları yaygın bir mat prop üzerinden bir SCNProgram'a aktarabilirim.

+1

ilgi Bunun cevabını bilmek, eğer mümkünse ... Şimdilik, köşe verimi depolamak için bir 'MTLBuffer' tarafından desteklenen bir 'SCNGeometrySource' yaratarak çalıştım. Verilmiş olan vertex verileri, daha sonra hesaplama gölgelendiricisi tarafından doğrudan değiştirilir. Özel bir köşe gölgelendiricisi yoktur ve dolayısıyla bir "MTLBuffer" içinde geçmeye gerek yoktur. – lock

cevap

-1

, arabelleklerin bağlantılarını adım adım adımdan değiştirebilirsiniz.

step1 computeCommandEncoder.setbuffer (ofset buffer1, 0, atIndex:) computeCommandEncoder.setBuffer (buffer2, ofset 0, atIndex:)

adım2 computeCommandEncoder.setBuffer (buffer1, ofset 0, atIndex:) computeCommandEncoder.setBuffer (buffer2, ofset 0, atIndex:)

step3 computeCommandEncoder.setBuffer (buffer1, ofset 0, atIndex:) computeCommandEncoder.setBuffer (ofset buffer2, 0, atIndex:) böylece

ve ... Hala

üzerinden tampona ve tersi de yeni olur

...

+0

Hesaplama işlevlerimin çoğu için bunu yapıyorum. Ancak bu durumda hesaplama işlevi iyi çalışır, soru bir çıktı gölgelendiricisinden çıktı alma ve render işlevine geçirme ile ilgilidir. SceneKit ile, özel gölgelendiricileri destekleyen 'SCNProgram' soyutlaması var, maalesef bir 'MTLBuffer' gibi bir şeyden geçmek için bir yöntem eksik görünüyor. SceneKit olmadan Metal kullanıyor olsaydım tam bir sorun olmaz. Teşekkürler – lock

+0

Üzgünüm, soruyu daha dikkatli okumalıydım. – Oliver

+0

Kaynak koduna yazdınız: // çalışmıyor let renderCommandEncoder = renderer.currentRenderCommandEncoder! renderCommandEncoder.setVertexBuffer (buffer2, offset: 0, atIndex: 2) renderCommandEncoder.endEncoding() Diğer iki arabelleği de (0 ve 1) renderEncoder'da mı ayarladınız? Yoksa bunu sadece hesaplama kodlayıcısı için yaptınız mı? Bunu denemedim, çünkü uygun iOS donanımı yok. OS X ile çalışıyorum. – Oliver