2015-08-31 21 views
5

के लिए मेमोरी बफर में स्विफ्ट ऐरे की कुशलतापूर्वक प्रतिलिपि बना रहा है मैं ऐप्पल के नए धातु ढांचे का उपयोग करके एक आईओएस एप्लिकेशन लिख रहा हूं। मेरे पास Matrix4 ऑब्जेक्ट्स की एक सरणी है (Ray Wenderlich's tutorial देखें) कि मुझे MTLDevice.newBufferWithLength() विधि के माध्यम से एक शेडर में पास करने की आवश्यकता है। मैट्रिक्स 4 ऑब्जेक्ट ऐप्पल के जीएलकिट का लाभ उठा रहा है (इसमें एक GLKMatrix4 ऑब्जेक्ट है)।आईओएस धातु

मैं जीपीयू कॉल के साथ इंस्टेंसिंग का लाभ उठा रहा हूं।

मैं बाद में एक struct जो (बस Matrix4 वस्तु परे उदाहरण के प्रति अधिक डेटा शामिल हैं को यह बदल जाएगा।

  1. मैं कुशलता से इस बफर में वस्तुओं की [Matrix4] सरणी कॉपी कर सकते हैं कैसे?

  2. वहाँ यह करने के लिए एक बेहतर तरीका है फिर से, मैं इस विस्तार करेंगे भविष्य में और अधिक डेटा के साथ एक struct का उपयोग करने के

नीचे मेरी कोड का एक सबसेट है?।:

let sizeofMatrix4 = sizeof(Float) * Matrix4.numberofElements() 

// This returns an array of [Matrix4] objects. 
let boxArray = createBoxArray(parentModelViewMatrix) 

let sizeOfUniformBuffer = boxArray.count * sizeOfMatrix4 
var uniformBuffer = device.newBufferWithLength(sizeofUniformBuffer, options: .CPUCacheModeDefaultCache) 
let bufferPointer = uniformBuffer?.contents() 

// Ouch - way too slow. How can I optimize? 
for i in 0..<boxArray.count 
{ 
    memcpy(bufferPointer! + (i * sizeOfMatrix4), boxArray[i].raw(), sizeOfMatrix4) 
} 

renderEncoder.setVertexBuffer(uniformBuffer, offset: 0, atIndex: 2) 

नोट: boxArray [i] .raw() विधि ऑब्जेक्टिव-सी कोड में इस के रूप में परिभाषित किया गया है:

- (void *)raw { 
    return glkMatrix.m; 
} 

आप देख सकते हैं मैं एक सरणी वस्तु के माध्यम से पाशन कर रहा हूँ और फिर एक memcpy कर रहे हैं। मैंने ऐसा इसलिए किया क्योंकि मुझे सरणी के एक संगत सेट के रूप में सरणी का इलाज करने में समस्याएं आ रही थीं।

धन्यवाद!

+1

आपको simd.float4x4 का उपयोग करना चाहिए। – Jessy

उत्तर

7

एक स्विफ्ट ऐरे को संगत स्मृति होने का वादा किया जाता है, लेकिन आपको यह सुनिश्चित करने की ज़रूरत है कि यह वास्तव में एक स्विफ्ट ऐरे है और गुप्त रूप से एनएसएआरएआरई नहीं है। यदि आप पूरी तरह से निश्चित होना चाहते हैं, तो एक ContiguousArray का उपयोग करें। यह संगत स्मृति सुनिश्चित करेगा भले ही इसमें वस्तुओं ओबीजेसी के लिए पुल योग्य हो। यदि आप स्मृति पर और अधिक नियंत्रण चाहते हैं, तो ManagedBuffer देखें।

इसके साथ, आपको अपनी मौजूदा मेमोरी के आसपास एमटीएल बफर बनाने के लिए newBufferWithBytesNoCopy(length:options:deallocator) का उपयोग करना चाहिए।

+0

रॉब, प्रतिक्रिया के लिए धन्यवाद। जब से आप एक प्रतिक्रिया पोस्ट करते हैं, तो मैं इसे काम करने की कोशिश कर रहा हूं लेकिन मुझे कोई भाग्य नहीं है। क्या आप स्रोत कोड प्रदान करना चाहते हैं कि आप [Matrix4] ऑब्जेक्ट्स की सरणी के साथ कैसे शुरू करते हैं, एमटीएलडीवीस बफर बनाएं और फिर उस बफर पर memcpy? –

+1

रोब, क्या आपको लगता है कि 'प्रबंधितबफर' पृष्ठ-संरेखित संग्रहण प्रदान करता है? स्रोत कोड और दस्तावेज़ीकरण पर एक सरसरी नज़र से पता चलता है कि यह केवल तत्व संरेखण को लागू करता है ('मेमोरीलेआउट । संरेखण') का सम्मान करके, पृष्ठ संरेखण नहीं। पेज संरेखण संभावित रूप से कहीं अधिक अपर्याप्त है और इसलिए मैं इसे स्पष्ट रूप से उल्लेख करने की अपेक्षा करता हूं। – ldoogy

4

मैंने इसे कणों की एक सरणी के साथ किया है जिसे मैं एक गणना शेडर को पास करता हूं।

संक्षेप में, मैं कुछ स्थिरांक को परिभाषित करने और परिवर्तनशील संकेत के एक मुट्ठी भर है और एक परिवर्तनशील बफर सूचक की घोषणा:

let particleCount: Int = 1048576 
var particlesMemory:UnsafeMutablePointer<Void> = nil 
let alignment:UInt = 0x4000 
let particlesMemoryByteSize:UInt = UInt(1048576) * UInt(sizeof(Particle)) 
var particlesVoidPtr: COpaquePointer! 
var particlesParticlePtr: UnsafeMutablePointer<Particle>! 

var particlesParticleBufferPtr: UnsafeMutableBufferPointer<Particle>! 

जब मैं कणों की स्थापना की है, मैं संकेत पॉप्युलेट और आवंटित करने के लिए posix_memalign() का उपयोग स्मृति:

posix_memalign(&particlesMemory, alignment, particlesMemoryByteSize) 

    particlesVoidPtr = COpaquePointer(particlesMemory) 
    particlesParticlePtr = UnsafeMutablePointer<Particle>(particlesVoidPtr) 

    particlesParticleBufferPtr = UnsafeMutableBufferPointer(start: particlesParticlePtr, count: particleCount) 

पाश कणों को भरने के लिए थोड़ा अलग है - अब मैं बफर सूचक अधिक पाश:

for index in particlesParticleBufferPtr.startIndex ..< particlesParticleBufferPtr.endIndex 
    { 
     [...] 

     let particle = Particle(positionX: positionX, positionY: positionY, velocityX: velocityX, velocityY: velocityY) 

     particlesParticleBufferPtr[index] = particle 
    } 

अंदर applyShader() फ़ंक्शन, मैं स्मृति जो दोनों इनपुट और आउटपुट बफर के रूप में प्रयोग किया जाता है की एक प्रतिलिपि बनाने:

let particlesBufferNoCopy = device.newBufferWithBytesNoCopy(particlesMemory, length: Int(particlesMemoryByteSize), 
     options: nil, deallocator: nil) 

    commandEncoder.setBuffer(particlesBufferNoCopy, offset: 0, atIndex: 0) 

    commandEncoder.setBuffer(particlesBufferNoCopy, offset: 0, atIndex: 1) 

...और बाद शेडर समाप्त हो गया है, मैं साझा स्मृति (particlesMemory) बफर सूचक में डाल वापस:

particlesVoidPtr = COpaquePointer(particlesMemory) 
    particlesParticlePtr = UnsafeMutablePointer(particlesVoidPtr) 

    particlesParticleBufferPtr = UnsafeMutableBufferPointer(start: particlesParticlePtr, count: particleCount) 

एक अद्यतित स्विफ्ट 2.0 इस at my GitHub repo here

+1

क्या आप स्विफ्ट 2 मतभेदों को रेखांकित कर सकते हैं? –

2

जाहिर की संस्करण साझा स्मृति का उपयोग कर की बात नहीं है और MTLDevice.makeBuffer(bytesNoCopy:...) अनावश्यक स्मृति प्रतियों से बचने के लिए है। इसलिए, आदर्श रूप में हम एक ऐसे डिज़ाइन की तलाश करते हैं जो हमें MTLBuffer ऑब्जेक्ट में लोड होने के बाद आसानी से डेटा में हेरफेर करने की अनुमति देता है।

थोड़ी देर के लिए शोध करने के बाद, मैंने पृष्ठ-संरेखित स्मृति के सरलीकृत आवंटन की अनुमति देने के लिए अर्ध-सामान्य समाधान बनाने और उस स्मृति में अपनी सामग्री को लोड करने, और बाद में साझा किए गए आइटमों में हेरफेर करने का निर्णय लिया है मेमोरी ब्लॉक

मैं एक स्विफ्ट सरणी कार्यान्वयन PageAlignedArray कहा जाता है कि इंटरफेस और की कार्यक्षमता से मेल खाता बना लिया है निर्मित स्विफ्ट सरणी, लेकिन हमेशा पेज गठबंधन स्मृति पर रहता है, और इसलिए बहुत आसानी से एक MTLBuffer में बनाया जा सकता है। मैंने मेटल बफर में PageAlignedArray को सीधे रूपांतरित करने के लिए एक सुविधा विधि भी जोड़ दी है।

बेशक, आप बाद में अपनी सरणी को बदलना जारी रख सकते हैं और आपके अपडेट स्वचालित रूप से साझा-स्मृति आर्किटेक्चर की GPU सौजन्य में उपलब्ध हो जाएंगे। हालांकि, ध्यान रखें कि जब भी सरणी की लंबाई बदल जाती है तो आपको अपने MTLBuffer ऑब्जेक्ट को पुन: उत्पन्न करना होगा।

यहां एक त्वरित कोड नमूना है:

var alignedArray : PageAlignedContiguousArray<matrix_double4x4> = [matrixTest, matrixTest] 
    alignedArray.append(item) 
    alignedArray.removeFirst() // Behaves just like a built-in array, with all convenience methods 

    // When it's time to generate a Metal buffer: 
    let testMetalBuffer = device?.makeBufferWithPageAlignedArray(alignedArray) 

नमूना का उपयोग करता matrix_double4x4, लेकिन सरणी किसी भी स्विफ्ट मूल्य प्रकार के लिए काम करना चाहिए। कृपया ध्यान दें कि यदि आप संदर्भ प्रकार का उपयोग करते हैं (जैसे किसी भी प्रकार का class), सरणी में आपके तत्वों के पॉइंटर्स होंगे और इसलिए आपके GPU कोड से उपयोग करने योग्य नहीं होंगे।

+1

शानदार !!! बस एक सवाल - मैं केवल एक उत्परिवर्तनीय सरणी बनाने के बारे में सोच रहा था जो एक परिवर्तनीय बफर पॉइंटर लेता है - क्या आपने यह विचार किया था, और यदि हां, तो आपने इसे क्यों अस्वीकार कर दिया? –

+1

@ डेविड एच इस तरह से स्थापित होने पर सरणी कैसे बढ़ेगी? मेरी अपनी कक्षा आवंटन करती थी ताकि सरणी को आवश्यकतानुसार बढ़ने की अनुमति मिल सके। – ldoogy

+1

बेशक आप सही हैं। मैं एक निश्चित आकार के परिवर्तनीय सरणी के बारे में सोच रहा था लेकिन निश्चित रूप से किसी को भी संलग्न करने की कोशिश करने से रोकने का कोई तरीका नहीं था। फिर, महान पद! –

संबंधित मुद्दे