Pada Mac dengan Apple silicon, lukis ke dalam MTLBuffer kongsi melalui CGContext bitmap, kemudian cipta tekstur Metal yang disokong oleh penimbal yang sama. Kontrak pentingnya ialah bait RGBA yang sepadan, stride baris yang sejajar, storan yang dikekalkan, serta akses CPU/GPU yang tersusun.

Panduan ini membina segi empat tepat merah dan bujur biru sintetik, kemudian menyemak bahawa Metal boleh membaca lukisan itu melalui tekstur. Panduan ini ditujukan kepada pembangun Mac yang menyediakan grafik terjana untuk pemapar Metal dalam app desktop. Mekanisme perkongsian ini didokumenkan oleh API Apple makeTexture(descriptor:offset:bytesPerRow:).

Laluan perkongsian yang mana patut anda pilih?

Peruntukkan storan dengan Metal terlebih dahulu. Dapatkan alamat CPU-nya dengan buffer.contents(), gunakan alamat itu sebagai destinasi konteks bitmap, kemudian cipta tekstur daripada penimbal. Apple mendokumenkan bahawa lukisan bitmap mengemas kini memori yang dibekalkan dan bahawa tekstur bersandarkan penimbal berkongsi peruntukan penimbal tersebut. Secara bersama, API ini menyediakan penghubung tanpa muat naik imej perantaraan. Graphics Contexts, MTLBuffer.contents(), MTLTexture.

Contoh ini secara jelas memerlukan GPU keluarga Apple. Dokumentasi storan semasa Apple menyatakan bahawa tekstur kongsi tidak tersedia pada GPU bukan keluarga Apple. Anggap sokongan GPU sebagai prasyarat, bukannya mengandaikan setiap Mac yang menyokong Metal juga menyokong strategi peruntukan khusus ini. MTLStorageMode.shared.

Keperluan anda Pendekatan yang sesuai
Core Graphics melukis; Metal membaca peruntukan yang sama pada Apple silicon Penimbal kongsi bersama tekstur bersandarkan penimbal, seperti di bawah
Tekstur yang diperuntukkan secara berasingan harus menerima lukisan Muat naik dengan replace(region:mipmapLevel:withBytes:bytesPerRow:) ke dalam tekstur yang boleh diakses CPU dan disokong
Penggunaan GPU berulang mendapat manfaat daripada tekstur persendirian yang berasingan Salin daripada sumber pementasan dengan pengekod blit
GPU yang dipilih tidak dapat mencipta tekstur kongsi Gunakan laluan muat naik atau salin pementasan yang disokong dan bukannya contoh tekstur kongsi ini

Alternatif ini mempunyai tingkah laku pemilikan dan penyalinan yang berbeza. Apple mendokumenkan replace sebagai salinan CPU serta-merta, manakala pengekod blit memindahkan data antara sumber, termasuk storan yang boleh diakses CPU dan storan persendirian. Laluan perkongsian langsung mengelakkan salinan muat naik itu, tetapi Apple mengingatkan bahawa tekstur bersandarkan penimbal mungkin menerima pengoptimuman tekstur yang lebih sedikit. Replacing texture pixels, MTLBlitCommandEncoder, Buffer-backed texture restrictions.

Tetapkan kontrak piksel, saiz dan pemilikan

Gunakan susun atur bait yang jelas: empat bait bagi setiap piksel mengikut tertib alamat yang meningkat R, G, B, A. Untuk Core Graphics, gabungkan premultipliedLast dengan byteOrder32Big; untuk Metal, gunakan rgba8Unorm. Tetapan tertib bait menjadikan tafsiran memori jelas, tanpa bergantung pada cara integer natif diwakilkan. Apple mendokumenkan kedudukan alfa, kawalan tertib bait dan empat komponen RGBA ternormal Metal secara berasingan. CGImageAlphaInfo.premultipliedLast, CGBitmapInfo, MTLPixelFormat.rgba8Unorm.

Alfa prapendaraban bermaksud saluran warna yang disimpan sudah merangkumi alfa. Sebagai pengiraan ilustrasi, merah penuh separa lutsinar di atas storan lutsinar adalah kira-kira 128, 0, 0, 128, dengan mengambil kira pengkuantuman. Syader hiliran mesti mengekalkan tafsiran itu; mendarab RGB sampel dengan alfa sekali lagi akan menggelapkannya. Contoh ini menggunakan bentuk legap supaya semakan baitnya mudah. Bitmap Images and Image Masks.

Contoh ini memilih CGColorSpace.linearSRGB, yang ditakrifkan Apple sebagai kolorimetri sRGB dengan fungsi pemindahan linear. Ini menetapkan tafsiran warna lukisan secara jelas; perkongsian memori itu sendiri tidak melakukan penukaran ruang warna. Kekalkan tafsiran itu secara konsisten apabila menyepadukan tekstur ke dalam pemapar. CGColorSpace.linearSRGB.

Kira stride baris sebelum memperuntukkan

Panjang baris yang kelihatan ialah width × 4, tetapi stride yang diperuntukkan mungkin lebih besar. Tanya minimumLinearTextureAlignment(for:) untuk peranti dan format yang dipilih, kemudian bundarkan panjang baris yang kelihatan ke atas kepada gandaan penjajaran itu. Berikan nilai bytesPerRow yang sama kepada Core Graphics dan Metal. minimumLinearTextureAlignment(for:).

Bagi penjajaran peranti ilustrasi yang dinyatakan secara jelas sebanyak 256 bait, imej 257 × 128 memerlukan 1,028 bait yang kelihatan bagi setiap baris, stride sejajar 1,280 bait dan 163,840 bait storan yang diperuntukkan. Tambahan 252 bait setiap baris ialah ruang padding, bukannya piksel tambahan. Program ini mendapatkan penjajaran sebenar dan bukannya mengandaikan nilai ilustrasi ini.

Peruntukkan sekurang-kurangnya bytesPerRow × height bait. Bagi piksel pada lajur memori x dan baris y, ofset baitnya ialah y × bytesPerRow + x × 4. Apabila menyesuaikan contoh tetap ini kepada dimensi berubah-ubah, tolak saiz bukan positif dan semak pendaraban serta pembundaran untuk limpahan integer sebelum peruntukan. Core Graphics mendokumenkan keperluan storan penuh hasil darab stride dengan tinggi. Creating a Bitmap Graphics Context.

Pastikan peruntukan kekal hidup sepanjang digunakan

Dalam reka bentuk ini, Metal memperuntukkan memori. Konteks bitmap meminjam alamatnya; pastikan penimbal kekal hidup sepanjang hayat konteks. Untuk kerja GPU tak segerak, kekalkan juga sumber sehingga kerja itu selesai. Jangan bebaskan buffer.contents() sendiri atau gantikannya dengan penuding yang diperoleh daripada akses tatasusunan Swift sementara.

Program ini menyelesaikan dan melepaskan konteks lukisan sebelum menghantar kerja GPU, kemudian memastikan penimbal dan tekstur tersedia sehingga kerja selesai. Ini mematuhi model memori bitmap yang dibekalkan pemanggil serta keperluan tertib CPU/GPU yang didokumenkan Metal. Graphics Contexts, Synchronizing CPU and GPU work.

Bina contoh lukisan sintetik yang lengkap

Cipta projek baris perintah macOS, atau simpan program lengkap berikut sebagai main.swift. Gunakan SDK macOS Xcode semasa: kod ini menggunakan pemula CGContext semasa yang argumen bitmapInfo-nya ialah CGBitmapInfo. SDK yang lebih lama mungkin mendedahkan pemula legasi yang menjangkakan bitmapInfo.rawValue. CGContext bitmap initializer.

Lukisan ini menggabungkan panggilan langsung Core Graphics dengan bujur AppKit. NSGraphicsContext(cgContext:flipped:) membalut konteks sedia ada supaya kedua-duanya melukis ke destinasi yang sama. Menyimpan dan memulihkan keadaan grafik AppKit memulihkan konteks semasa sebelumnya selepas lukisan. 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")
}

Kompil dan jalankannya pada Mac sasaran:

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

Sifat buffer tekstur menyemak sumber sandarannya. Diagnostik itu kemudian menyalin piksel tekstur ke dalam penimbal kongsi yang berasingan dan menyemak sudut merah yang tidak disentuh selepas GPU selesai. Bacaan balik itu ialah salinan ujian yang disengajakan; ia berasingan daripada peruntukan lukisan kongsi. Apple mendokumenkan operasi salin tekstur ke penimbal dan menetapkan destinationBytesPerImage: 0 untuk salinan 2D. MTLTexture.buffer, Texture-to-buffer copy.

Pengiraan stride disemak menggunakan input sampel yang deterministik. Program Swift itu diteliti berdasarkan API yang diperoleh, tetapi tidak dapat dikompil atau dijalankan dalam persekitaran penyelidikan yang menyediakan Linux tanpa Swift atau rangka kerja Apple.

Selaraskan lukisan CPU dan penggunaan GPU

Storan kongsi masih memerlukan jadual akses. Selesaikan lukisan CPU sebelum menyerahkan arahan yang membaca tekstur. Selepas penghantaran, jangan ubah peruntukan itu sehingga GPU selesai menggunakannya. Apple secara jelas mewajibkan penyelarasan ini untuk sumber kongsi. MTLStorageMode.shared.

Untuk contoh sekali jalan, waitUntilCompleted() memudahkan pemeriksaan serahan: ia menyekat pelaksanaan sehingga penimbal arahan dan pengendali penyelesaiannya selesai. Dalam pemapar interaktif, elakkan menunggu secara menyekat pada setiap bingkai. Sebaliknya, kekalkan berbilang pasangan penimbal dan tekstur, dan kembalikan setiap pasangan ke dalam kumpulan yang tersedia hanya selepas pemberitahuan penyelesaian GPU diterima. Apple menunjukkan corak kumpulan sumber ini dalam sampel penyelarasannya. waitUntilCompleted(), Synchronizing CPU and GPU work.

Jika kemudian GPU menulis melalui satu alias dan membaca melalui alias yang lain, semak semula penyelarasan. Apple menyatakan bahawa data tekstur bersandarkan penimbal boleh konsisten antara laluan render tanpa mencerminkan perubahan alias dengan serta-merta dalam satu laluan. Contoh ini mengelakkan kerumitan itu dengan menyelesaikan lukisan CPU sebelum operasi bacaan GPU tunggalnya. Buffer-backed texture coherence.

Diagnosis masalah susun atur sebelum mengubah pemapar

Gunakan urutan berikut untuk mengenal pasti kontrak yang gagal. Semakan ini berpunca daripada peraturan susun atur piksel, sumber sandaran, stride dan penyegerakan yang didokumenkan di atas.

  1. Penciptaan tekstur gagal: pastikan GPU menyokong tekstur kongsi, kedua-dua sumber menggunakan .shared, serta ofset dan stride memenuhi penjajaran yang ditanya. Pastikan tekstur 2D, sampel tunggal, dengan satu aras mip dan satu unsur tatasusunan.
  2. Merah kelihatan biru: periksa bait satu demi satu. Contoh ini memerlukan tertib bait RGBA, premultipliedLast, byteOrder32Big dan .rgba8Unorm secara bersama.
  3. Baris kelihatan senget: bandingkan stride konteks dengan stride tekstur. Kedua-duanya mesti menggunakan rowBytes, termasuk padding.
  4. Imej kelihatan terbalik: semak transformasi lukisan dan pemetaan koordinat tekstur pemapar. Contoh ini menggunakan konteks lukisan yang tidak diterbalikkan. Argumen flipped AppKit menerangkan keadaan awalnya yang terbalik; argumen itu tidak menyusun semula baris yang disimpan.
  5. Kemas kini kelihatan tidak konsisten: semak sama ada lukisan CPU bertindih dengan akses GPU kepada peruntukan yang sama. Gunakan penjejakan penyelesaian sebelum menggunakannya semula.

Kekangan penciptaan datang daripada dokumentasi tekstur linear Apple, manakala perbezaan keadaan terbalik datang daripada pemula AppKit. Kekalkan pengendalian orientasi secara jelas apabila menambah syader paparan anda sendiri. Linear-texture requirements, AppKit flipped-state parameter.

Setelah penimbal sandaran, susun atur bait, stride berpemad dan jadual akses sepadan, lukisan sintetik itu tersedia kepada Metal melalui tekstur kongsi. Kekalkan kontrak tersebut apabila mengubah saiz imej atau menambah operasi lukisan; pilih salinan pementasan apabila GPU sasaran atau keperluan tekstur hiliran memerlukan storan berasingan.

Sumber