Apple silicon Mac पर, बिटमैप CGContext के ज़रिए साझा MTLBuffer में ड्रॉ करें, फिर उसी बफ़र के पीछे समर्थित Metal टेक्सचर बनाएँ। ज़रूरी शर्तें हैं: मेल खाते RGBA बाइट, संरेखित पंक्ति स्ट्राइड, बरकरार रखा गया स्टोरेज, और क्रमबद्ध CPU/GPU एक्सेस।

यह गाइड एक कृत्रिम लाल आयत और नीला अंडाकार बनाती है, फिर जाँचती है कि Metal टेक्सचर के ज़रिए ड्रॉइंग पढ़ सकता है। यह उस Mac डेवलपर के लिए है जो डेस्कटॉप ऐप के Metal रेंडरर के लिए जनरेट किए गए ग्राफ़िक्स तैयार कर रहा है। साझा करने की यह विधि Apple के makeTexture(descriptor:offset:bytesPerRow:) API में दस्तावेज़ीकृत है।

साझा करने का कौन-सा तरीका चुनें?

पहले Metal से स्टोरेज आवंटित करें। buffer.contents() से उसका CPU पता प्राप्त करें, उस पते को बिटमैप कॉन्टेक्स्ट के गंतव्य के रूप में इस्तेमाल करें, और बफ़र से टेक्सचर बनाएँ। Apple दस्तावेज़ीकृत करता है कि बिटमैप ड्रॉइंग दिए गए मेमोरी को अपडेट करती है और बफ़र-समर्थित टेक्सचर अपने बफ़र का आवंटन साझा करता है। ये API मिलकर बीच में इमेज अपलोड किए बिना दोनों के बीच पुल बनाते हैं। Graphics Contexts, MTLBuffer.contents(), MTLTexture।

यह उदाहरण स्पष्ट रूप से Apple परिवार के GPU की माँग करता है। Apple के मौजूदा स्टोरेज दस्तावेज़ के अनुसार, गैर-Apple परिवार के GPU पर साझा टेक्सचर उपलब्ध नहीं हैं। यह मानने के बजाय कि Metal-सक्षम हर Mac इस आवंटन रणनीति का समर्थन करता है, GPU समर्थन को पूर्वशर्त मानें। MTLStorageMode.shared।

आपकी आवश्यकता उपयुक्त तरीका
Core Graphics ड्रॉ करे; Apple silicon पर Metal उसी आवंटन को पढ़े नीचे दिए अनुसार साझा बफ़र और बफ़र-समर्थित टेक्सचर
अलग से आवंटित टेक्सचर में ड्रॉइंग डालनी हो समर्थित CPU-सुलभ टेक्सचर में replace(region:mipmapLevel:withBytes:bytesPerRow:) से अपलोड करें
बार-बार GPU इस्तेमाल के लिए अलग निजी टेक्सचर फ़ायदेमंद हो ब्लिट एन्कोडर से स्टेजिंग रिसोर्स की कॉपी करें
चुना गया GPU साझा टेक्सचर नहीं बना सकता इस साझा-टेक्सचर उदाहरण के बजाय समर्थित अपलोड या स्टेजिंग-कॉपी तरीका अपनाएँ

इन विकल्पों में स्वामित्व और कॉपी करने का व्यवहार अलग-अलग है। Apple replace को तत्काल CPU कॉपी के रूप में दस्तावेज़ीकृत करता है, जबकि ब्लिट एन्कोडर CPU-सुलभ और निजी स्टोरेज समेत रिसोर्सों के बीच डेटा ले जाते हैं। सीधे साझा करने का तरीका अपलोड कॉपी से बचाता है, लेकिन Apple चेतावनी देता है कि बफ़र-समर्थित टेक्सचर को टेक्सचर के कुछ ऑप्टिमाइज़ेशन कम मिल सकते हैं। Replacing texture pixels, MTLBlitCommandEncoder, Buffer-backed texture restrictions।

पिक्सेल, आकार और स्वामित्व की शर्तें तय करें

बाइट लेआउट स्पष्ट रखें: बढ़ते मेमोरी पते के क्रम में हर पिक्सेल के लिए चार बाइट, R, G, B, A। Core Graphics के लिए premultipliedLast को byteOrder32Big के साथ जोड़ें; Metal के लिए rgba8Unorm इस्तेमाल करें। बाइट-क्रम की सेटिंग यह स्पष्ट करती है कि मेमोरी को कैसे समझना है, और यह इस बात पर निर्भर नहीं करती कि नेटिव इंटीजर को कैसे दर्शाया जाएगा। Apple अल्फ़ा की स्थिति, बाइट-क्रम नियंत्रण और Metal के चार नॉर्मलाइज़्ड RGBA कॉम्पोनेंट को अलग-अलग दस्तावेज़ीकृत करता है। CGImageAlphaInfo.premultipliedLast, CGBitmapInfo, MTLPixelFormat.rgba8Unorm।

प्रीमल्टिप्लाइड अल्फ़ा का अर्थ है कि स्टोर किए गए रंग चैनलों में अल्फ़ा पहले से शामिल है। उदाहरण के तौर पर, पारदर्शी स्टोरेज पर आधी पारदर्शिता वाला पूरा लाल रंग लगभग 128, 0, 0, 128 बनता है; इसमें क्वांटाइज़ेशन का असर शामिल हो सकता है। आगे इस्तेमाल होने वाले शेडर को इस अर्थ को बनाए रखना चाहिए; सैंपल किए गए RGB को दोबारा अल्फ़ा से गुणा करने पर रंग गहरा हो जाएगा। बाइट जाँच सरल रखने के लिए उदाहरण में अपारदर्शी आकार हैं। Bitmap Images and Image Masks।

यह उदाहरण CGColorSpace.linearSRGB चुनता है, जिसे Apple रैखिक ट्रांसफ़र फ़ंक्शन वाले sRGB कलरिमेट्री के रूप में परिभाषित करता है। इससे ड्रॉइंग की रंग-व्याख्या स्पष्ट रूप से तय होती है; मेमोरी साझा करने से रंग-स्थान का रूपांतरण नहीं होता। टेक्सचर को रेंडरर में जोड़ते समय यही व्याख्या बनाए रखें। CGColorSpace.linearSRGB।

आवंटन से पहले पंक्ति स्ट्राइड की गणना करें

दिखाई देने वाली पंक्ति की लंबाई width × 4 है, लेकिन आवंटित स्ट्राइड इससे बड़ा हो सकता है। चुने गए डिवाइस और फ़ॉर्मैट के लिए minimumLinearTextureAlignment(for:) से अलाइनमेंट पूछें, फिर दिखाई देने वाली पंक्ति की लंबाई को उस अलाइनमेंट के अगले गुणज तक ऊपर राउंड करें। Core Graphics और Metal, दोनों को वही परिणामी bytesPerRow दें। minimumLinearTextureAlignment(for:)।

256 बाइट के स्पष्ट रूप से उदाहरणार्थ डिवाइस अलाइनमेंट के लिए, 257 × 128 इमेज को प्रति पंक्ति 1,028 दिखाई देने वाले बाइट, 1,280 बाइट का अलाइन्ड स्ट्राइड और कुल 163,840 आवंटित बाइट चाहिए। हर पंक्ति के अतिरिक्त 252 बाइट पेडिंग हैं, अतिरिक्त पिक्सेल नहीं। प्रोग्राम इस उदाहरणार्थ मान को मानकर चलने के बजाय वास्तविक अलाइनमेंट पूछता है।

कम-से-कम bytesPerRow × height बाइट आवंटित करें। मेमोरी कॉलम x और पंक्ति y के पिक्सेल का बाइट ऑफ़सेट y × bytesPerRow + x × 4 होता है। इस निश्चित उदाहरण को बदलते आयामों के लिए अनुकूलित करते समय, गैर-सकारात्मक आकार अस्वीकार करें और आवंटन से पहले गुणा तथा राउंडिंग में इंटीजर ओवरफ़्लो की जाँच करें। Core Graphics स्ट्राइड और ऊँचाई के गुणनफल जितने स्टोरेज की पूरी आवश्यकता दस्तावेज़ीकृत करता है। Creating a Bitmap Graphics Context।

हर उपभोक्ता के इस्तेमाल तक आवंटन को जीवित रखें

इस डिज़ाइन में मेमोरी Metal आवंटित करता है। बिटमैप कॉन्टेक्स्ट उसका पता उधार लेता है; कॉन्टेक्स्ट के पूरे जीवनकाल तक बफ़र जीवित रखें। GPU का काम असिंक्रोनस हो तो वह काम पूरा होने तक रिसोर्सों को भी बनाए रखें। buffer.contents() को खुद मुक्त न करें और अस्थायी Swift ऐरे एक्सेस से मिला पॉइंटर उसकी जगह इस्तेमाल न करें।

प्रोग्राम GPU काम सबमिट करने से पहले ड्रॉइंग कॉन्टेक्स्ट पूरा करके रिलीज़ करता है, फिर काम पूरा होने तक बफ़र और टेक्सचर उपलब्ध रखता है। यह कॉलर द्वारा दिए गए बिटमैप-मेमोरी मॉडल और CPU/GPU के क्रम से जुड़े Metal के दस्तावेज़ीकृत नियमों का पालन करता है। Graphics Contexts, Synchronizing CPU and GPU work।

एक संपूर्ण कृत्रिम ड्रॉइंग उदाहरण बनाएँ

macOS कमांड-लाइन प्रोजेक्ट बनाएँ, या नीचे दिए गए पूरे प्रोग्राम को main.swift के रूप में सेव करें। मौजूदा Xcode macOS SDK इस्तेमाल करें: कोड मौजूदा CGContext इनिशियलाइज़र का उपयोग करता है, जिसके bitmapInfo आर्ग्युमेंट का प्रकार CGBitmapInfo है। पुराने SDK में हो सकता है कि इसके बजाय bitmapInfo.rawValue अपेक्षित करने वाला पुराना इनिशियलाइज़र उपलब्ध हो। CGContext bitmap initializer।

ड्रॉइंग में सीधे Core Graphics कॉल और AppKit अंडाकार, दोनों शामिल हैं। NSGraphicsContext(cgContext:flipped:) मौजूदा कॉन्टेक्स्ट को रैप करता है, इसलिए दोनों एक ही गंतव्य पर ड्रॉ करते हैं। AppKit की ग्राफ़िक्स स्थिति को सेव और रीस्टोर करने से ड्रॉइंग के बाद पिछला मौजूदा कॉन्टेक्स्ट वापस आ जाता है। NSGraphicsContext initializer, saveGraphicsState(), restoreGraphicsState()।

import AppKit
import CoreGraphics
import Metal

// Fixed, small dimensions keep all size arithmetic bounded.
let width = 257
let height = 128
let format: MTLPixelFormat = .rgba8Unorm

guard let device = MTLCreateSystemDefaultDevice(),
      device.supportsFamily(.apple1) else {
    fatalError("This example requires an Apple-family GPU.")
}
let alignment = device.minimumLinearTextureAlignment(for: format)
guard alignment > 0 else { fatalError("Invalid alignment.") }
let rowBytes = ((width * 4 + alignment - 1) / alignment) * alignment
let byteCount = rowBytes * height

guard let buffer = device.makeBuffer(
    length: byteCount, options: .storageModeShared
) else { fatalError("Drawing buffer allocation failed.") }

let descriptor = MTLTextureDescriptor.texture2DDescriptor(
    pixelFormat: format, width: width, height: height, mipmapped: false
)
descriptor.storageMode = .shared
descriptor.usage = .shaderRead

guard let texture = buffer.makeTexture(
    descriptor: descriptor, offset: 0, bytesPerRow: rowBytes
) else { fatalError("Buffer-backed texture creation failed.") }
precondition(texture.buffer === buffer)

// This scope finishes CPU drawing before GPU submission.
do {
    guard let space = CGColorSpace(name: CGColorSpace.linearSRGB) else {
        fatalError("Color space creation failed.")
    }
    let info = CGBitmapInfo(
        rawValue: CGImageAlphaInfo.premultipliedLast.rawValue
    ).union(.byteOrder32Big)
    guard let cg = CGContext(
        data: buffer.contents(), width: width, height: height,
        bitsPerComponent: 8, bytesPerRow: rowBytes,
        space: space, bitmapInfo: info
    ) else { fatalError("Bitmap context creation failed.") }

    cg.setFillColor(red: 1, green: 0, blue: 0, alpha: 1)
    cg.fill(CGRect(x: 0, y: 0,
                   width: CGFloat(width), height: CGFloat(height)))

    NSGraphicsContext.saveGraphicsState()
    defer { NSGraphicsContext.restoreGraphicsState() }
    NSGraphicsContext.current = NSGraphicsContext(
        cgContext: cg, flipped: false
    )
    NSColor.blue.setFill()
    NSBezierPath(ovalIn: NSRect(x: 32, y: 24, width: 48, height: 48)).fill()
}

// Diagnostic copy: prove the GPU can read through the texture.
guard let readback = device.makeBuffer(
    length: byteCount, options: .storageModeShared
), let queue = device.makeCommandQueue(),
   let command = queue.makeCommandBuffer(),
   let blit = command.makeBlitCommandEncoder() else {
    fatalError("Readback setup failed.")
}
blit.copy(
    from: texture, sourceSlice: 0, sourceLevel: 0,
    sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
    sourceSize: MTLSize(width: width, height: height, depth: 1),
    to: readback, destinationOffset: 0,
    destinationBytesPerRow: rowBytes, destinationBytesPerImage: 0
)
blit.endEncoding()
command.commit()
command.waitUntilCompleted()
guard command.status == .completed else {
    fatalError("GPU copy failed: \(String(describing: command.error))")
}
let pixel = readback.contents().assumingMemoryBound(to: UInt8.self)
precondition(pixel[0] == 255 && pixel[1] == 0 &&
             pixel[2] == 0 && pixel[3] == 255)
withExtendedLifetime(buffer) {
    print("Shared RGBA texture verified; row stride: \(rowBytes) bytes")
}

लक्ष्य Mac पर इसे कंपाइल और चलाएँ:

xcrun swiftc main.swift -o SharedRGBA -framework AppKit -framework CoreGraphics -framework Metal
./SharedRGBA

टेक्सचर की buffer प्रॉपर्टी उसके बैकिंग रिसोर्स की जाँच करती है। फिर डायग्नॉस्टिक टेक्सचर के पिक्सेल को अलग साझा बफ़र में कॉपी करता है और GPU के पूरा होने के बाद एक ऐसे लाल कोने की जाँच करता है जिस पर ड्रॉ नहीं हुआ। यह रीडबैक जानबूझकर की गई टेस्ट कॉपी है; यह साझा ड्रॉइंग आवंटन से अलग है। Apple टेक्सचर से बफ़र में कॉपी करने का तरीका दस्तावेज़ीकृत करता है और 2D कॉपी के लिए destinationBytesPerImage: 0 निर्दिष्ट करता है। MTLTexture.buffer, Texture-to-buffer copy।

स्ट्राइड की गणनाओं को नियतात्मक नमूना इनपुट से जाँचा गया। प्राप्त API के संदर्भ में Swift प्रोग्राम का निरीक्षण किया गया, लेकिन शोध परिवेश में Swift या Apple फ़्रेमवर्क के बिना Linux उपलब्ध होने के कारण इसे कंपाइल या चलाया नहीं जा सका।

CPU ड्रॉइंग और GPU उपयोग का तालमेल करें

साझा स्टोरेज के लिए भी एक्सेस का क्रम तय करना ज़रूरी है। टेक्सचर पढ़ने वाले कमांड कमिट करने से पहले CPU ड्रॉइंग पूरी करें। सबमिशन के बाद, जब तक GPU उस आवंटन का इस्तेमाल कर रहा हो, उसे न बदलें। Apple साझा रिसोर्सों के लिए इस तालमेल की स्पष्ट माँग करता है। MTLStorageMode.shared।

एक बार चलने वाले इस उदाहरण के लिए waitUntilCompleted() हैंडऑफ़ को आसानी से जाँचने योग्य बनाता है: यह तब तक ब्लॉक करता है जब तक कमांड बफ़र और उसके कम्प्लीशन हैंडलर पूरे न हो जाएँ। इंटरैक्टिव रेंडरर में हर फ़्रेम पर यह ब्लॉकिंग वेट न रखें। इसके बजाय, बफ़र और टेक्सचर के कई जोड़े बनाए रखें और GPU कम्प्लीशन नोटिफ़िकेशन मिलने के बाद ही हर जोड़े को उपलब्ध पूल में लौटाएँ। Apple अपने सिंक्रोनाइज़ेशन सैंपल में यही रिसोर्स-पूल पैटर्न दिखाता है। waitUntilCompleted(), Synchronizing CPU and GPU work।

अगर बाद में GPU एक एलियास के ज़रिए लिखे और दूसरे से पढ़े, तो सिंक्रोनाइज़ेशन की फिर से समीक्षा करें। Apple बताता है कि बफ़र-समर्थित टेक्सचर का डेटा रेंडर पास के बीच कोहेरेंट हो सकता है, लेकिन एक ही पास के भीतर एलियास में हुए बदलाव तुरंत दिखाई नहीं देते। उदाहरण इस जटिलता से बचता है, क्योंकि इसमें एक GPU रीड ऑपरेशन से पहले CPU ड्रॉइंग पूरी हो जाती है। Buffer-backed texture coherence।

रेंडरर बदलने से पहले लेआउट की समस्याएँ पहचानें

नाकाम हुई शर्त को अलग करके पहचानने के लिए यह क्रम अपनाएँ। ये जाँचें ऊपर दिए गए पिक्सेल लेआउट, बैकिंग रिसोर्स, स्ट्राइड और सिंक्रोनाइज़ेशन के दस्तावेज़ीकृत नियमों पर आधारित हैं।

  1. टेक्सचर नहीं बनता: पुष्टि करें कि GPU साझा टेक्सचर का समर्थन करता है, दोनों रिसोर्स .shared इस्तेमाल करते हैं और ऑफ़सेट व स्ट्राइड पूछे गए अलाइनमेंट पर खरे उतरते हैं। टेक्सचर को 2D, सिंगल-सैंपल, एक मिप स्तर और एक ऐरे एलिमेंट वाला रखें।
  2. लाल नीला दिखता है: अलग-अलग बाइट जाँचें। उदाहरण के लिए RGBA बाइट क्रम में premultipliedLast, byteOrder32Big और .rgba8Unorm का एक साथ इस्तेमाल ज़रूरी है।
  3. पंक्तियाँ तिरछी दिखती हैं: कॉन्टेक्स्ट और टेक्सचर के स्ट्राइड की तुलना करें। पेडिंग समेत, दोनों में rowBytes होना चाहिए।
  4. इमेज उलटी दिखती है: ड्रॉइंग ट्रांसफ़ॉर्म और रेंडरर की टेक्सचर-कोऑर्डिनेट मैपिंग जाँचें। उदाहरण अनफ़्लिप्ड ड्रॉइंग कॉन्टेक्स्ट इस्तेमाल करता है। AppKit का flipped आर्ग्युमेंट उसके शुरुआती फ़्लिप्ड स्टेट को बताता है; यह स्टोर की गई पंक्तियों का क्रम नहीं बदलता।
  5. अपडेट कभी दिखते हैं, कभी नहीं: जाँचें कि CPU ड्रॉइंग उसी आवंटन तक GPU की पहुँच के साथ ओवरलैप तो नहीं कर रही। उसे दोबारा इस्तेमाल करने से पहले कम्प्लीशन ट्रैकिंग अपनाएँ।

बनाने की शर्तें Apple के लीनियर-टेक्सचर दस्तावेज़ से आती हैं, जबकि फ़्लिप्ड-स्टेट का अंतर AppKit इनिशियलाइज़र में बताया गया है। अपना डिस्प्ले शेडर जोड़ते समय ओरिएंटेशन को स्पष्ट रूप से संभालें। Linear-texture requirements, AppKit flipped-state parameter।

बैकिंग बफ़र, बाइट लेआउट, पेडेड स्ट्राइड और एक्सेस का क्रम मेल खाने पर कृत्रिम ड्रॉइंग साझा टेक्सचर के ज़रिए Metal को उपलब्ध हो जाती है। इमेज का आकार बदलते या और ड्रॉइंग ऑपरेशन जोड़ते समय इन शर्तों को बनाए रखें; लक्ष्य GPU या आगे के टेक्सचर की ज़रूरतों के अनुसार अलग स्टोरेज चाहिए तो स्टेजिंग कॉपी चुनें।

स्रोत