Di Mac dengan Apple silicon, gambar ke MTLBuffer bersama melalui CGContext bitmap, lalu buat tekstur Metal yang didukung oleh buffer yang sama. Kontrak utamanya adalah byte RGBA yang cocok, stride baris yang selaras, penyimpanan yang tetap dipertahankan, serta akses CPU/GPU yang terurut.
Panduan ini membuat persegi panjang merah dan oval biru sintetis, lalu memeriksa apakah Metal dapat membaca gambar melalui tekstur tersebut. Panduan ini ditujukan bagi pengembang Mac yang menyiapkan grafis hasil generasi untuk renderer Metal aplikasi desktop. Mekanisme berbagi ini didokumentasikan dalam API makeTexture(descriptor:offset:bytesPerRow:) milik Apple.
Jalur berbagi mana yang sebaiknya dipilih?
Alokasikan penyimpanan dengan Metal terlebih dahulu. Dapatkan alamat CPU-nya dengan buffer.contents(), gunakan alamat tersebut sebagai tujuan konteks bitmap, lalu buat tekstur dari buffer. Apple mendokumentasikan bahwa penggambaran bitmap memperbarui memori yang diberikan dan bahwa tekstur yang didukung buffer berbagi alokasi buffer tersebut. Kedua API ini menyediakan jembatan tanpa perlu mengunggah gambar perantara. Graphics Contexts, MTLBuffer.contents(), MTLTexture.
Contoh ini secara eksplisit mensyaratkan GPU keluarga Apple. Dokumentasi penyimpanan Apple saat ini menyatakan bahwa tekstur bersama tidak tersedia pada GPU nonkeluarga Apple. Perlakukan dukungan GPU sebagai prasyarat, alih-alih berasumsi bahwa setiap Mac yang mendukung Metal juga mendukung strategi alokasi persis ini. MTLStorageMode.shared.
| Kebutuhan Anda | Pendekatan yang sesuai |
|---|---|
| Core Graphics menggambar; Metal membaca alokasi yang sama pada Apple silicon | Buffer bersama ditambah tekstur yang didukung buffer, seperti di bawah |
| Tekstur yang dialokasikan secara terpisah harus menerima gambar | Unggah dengan replace(region:mipmapLevel:withBytes:bytesPerRow:) ke tekstur yang mendukung akses CPU |
| Penggunaan GPU berulang lebih optimal dengan tekstur privat terpisah | Salin dari sumber staging menggunakan blit encoder |
| GPU yang dipilih tidak dapat membuat tekstur bersama | Gunakan jalur unggah atau salin staging yang didukung sebagai pengganti contoh tekstur bersama ini |
Alternatif-alternatif ini memiliki perilaku kepemilikan dan penyalinan yang berbeda. Apple mendokumentasikan replace sebagai penyalinan CPU langsung, sedangkan blit encoder memindahkan data antarresource, termasuk penyimpanan yang dapat diakses CPU dan penyimpanan privat. Jalur berbagi langsung menghindari penyalinan saat unggah tersebut, tetapi Apple mengingatkan bahwa tekstur yang didukung buffer mungkin mendapatkan lebih sedikit optimasi tekstur. Replacing texture pixels, MTLBlitCommandEncoder, Buffer-backed texture restrictions.
Tetapkan kontrak piksel, ukuran, dan kepemilikan
Gunakan tata letak byte yang eksplisit: empat byte per piksel dalam urutan alamat yang meningkat, R, G, B, A. Untuk Core Graphics, gabungkan premultipliedLast dengan byteOrder32Big; untuk Metal, gunakan rgba8Unorm. Pengaturan urutan byte memperjelas interpretasi memori, terlepas dari bagaimana bilangan bulat native direpresentasikan. Apple mendokumentasikan posisi alfa, kontrol urutan byte, dan empat komponen RGBA ternormalisasi Metal secara terpisah. CGImageAlphaInfo.premultipliedLast, CGBitmapInfo, MTLPixelFormat.rgba8Unorm.
Alfa premultiplikasi berarti kanal warna yang tersimpan sudah menyertakan alfa. Sebagai contoh perhitungan, merah penuh dengan transparansi setengah di atas penyimpanan transparan akan menghasilkan kira-kira 128, 0, 0, 128, dengan mempertimbangkan kuantisasi. Shader berikutnya harus mempertahankan interpretasi tersebut; mengalikan RGB yang disampel dengan alfa sekali lagi akan membuatnya lebih gelap. Contoh ini menggunakan bentuk buram agar pemeriksaan byte tetap sederhana. Bitmap Images and Image Masks.
Contoh ini memilih CGColorSpace.linearSRGB, yang didefinisikan Apple sebagai kolorimetri sRGB dengan fungsi transfer linear. Pilihan ini menetapkan interpretasi warna gambar secara eksplisit; berbagi memori itu sendiri tidak melakukan konversi ruang warna. Pertahankan interpretasi tersebut agar konsisten saat mengintegrasikan tekstur ke dalam renderer. CGColorSpace.linearSRGB.
Hitung stride baris sebelum mengalokasikan
Panjang baris yang terlihat adalah width × 4, tetapi stride yang dialokasikan mungkin lebih besar. Tanyakan minimumLinearTextureAlignment(for:) untuk perangkat dan format yang dipilih, lalu bulatkan panjang baris yang terlihat ke atas hingga kelipatan alignment tersebut. Berikan bytesPerRow hasil yang sama kepada Core Graphics dan Metal. minimumLinearTextureAlignment(for:).
Dengan alignment perangkat ilustratif sebesar 256 byte, gambar berukuran 257 × 128 memerlukan 1,028 byte yang terlihat per baris, stride selaras sebesar 1,280 byte, dan total alokasi 163,840 byte. Tambahan 252 byte per baris adalah padding, bukan piksel tambahan. Program menanyakan alignment sebenarnya alih-alih mengasumsikan nilai ilustratif ini.
Alokasikan sedikitnya bytesPerRow × height byte. Untuk piksel pada kolom memori x dan baris y, offset byte-nya adalah y × bytesPerRow + x × 4. Saat mengadaptasi contoh berukuran tetap ini ke dimensi variabel, tolak ukuran nonpositif dan periksa perkalian serta pembulatan terhadap luapan bilangan bulat sebelum alokasi. Core Graphics mendokumentasikan kebutuhan penyimpanan penuh sebesar stride dikalikan tinggi. Creating a Bitmap Graphics Context.
Pertahankan alokasi selama digunakan oleh semua pihak
Dalam rancangan ini, Metal mengalokasikan memori. Konteks bitmap meminjam alamatnya; pertahankan buffer tetap hidup selama seluruh masa pakai konteks. Untuk pekerjaan GPU asinkron, pertahankan juga resource hingga pekerjaan tersebut selesai. Jangan membebaskan buffer.contents() sendiri atau menggantinya dengan pointer yang diperoleh dari akses sementara ke array Swift.
Program menyelesaikan dan melepaskan konteks gambar sebelum mengirim pekerjaan GPU, lalu mempertahankan buffer dan tekstur hingga pekerjaan selesai. Ini mengikuti model memori bitmap yang disediakan pemanggil dan persyaratan urutan CPU/GPU yang didokumentasikan Metal. Graphics Contexts, Synchronizing CPU and GPU work.
Buat contoh gambar sintetis yang lengkap
Buat proyek baris perintah macOS, atau simpan program lengkap berikut sebagai main.swift. Gunakan SDK macOS Xcode terkini: kode ini menggunakan inisialisasi CGContext terkini dengan argumen bitmapInfo bertipe CGBitmapInfo. SDK yang lebih lama mungkin menyediakan inisialisasi lama yang mengharapkan bitmapInfo.rawValue. CGContext bitmap initializer.
Gambar ini menggabungkan pemanggilan langsung Core Graphics dengan oval AppKit. NSGraphicsContext(cgContext:flipped:) membungkus konteks yang sudah ada, sehingga keduanya menggambar ke tujuan yang sama. Menyimpan dan memulihkan status grafis AppKit akan memulihkan konteks aktif sebelumnya setelah penggambaran. 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")
}
Kompilasi dan jalankan di Mac target:
xcrun swiftc main.swift -o SharedRGBA -framework AppKit -framework CoreGraphics -framework Metal
./SharedRGBA
Properti buffer pada tekstur memeriksa resource pendukungnya. Diagnostik kemudian menyalin piksel tekstur ke buffer bersama terpisah dan memeriksa sudut merah yang tidak disentuh setelah GPU selesai. Pembacaan balik itu merupakan penyalinan untuk pengujian; penyalinan tersebut terpisah dari alokasi gambar bersama. Apple mendokumentasikan operasi tekstur ke buffer dan menetapkan destinationBytesPerImage: 0 untuk penyalinan 2D. MTLTexture.buffer, Texture-to-buffer copy.
Perhitungan stride diperiksa menggunakan masukan sampel deterministik. Program Swift ditelaah berdasarkan API yang diperoleh, tetapi tidak dapat dikompilasi atau dijalankan di lingkungan riset yang menyediakan Linux tanpa Swift atau framework Apple.
Selaraskan penggambaran CPU dan penggunaan GPU
Penyimpanan bersama tetap memerlukan jadwal akses. Selesaikan penggambaran CPU sebelum mengirim perintah yang membaca tekstur. Setelah pengiriman, jangan ubah alokasi tersebut sampai GPU selesai menggunakannya. Apple secara eksplisit mensyaratkan koordinasi ini untuk resource bersama. MTLStorageMode.shared.
Untuk contoh sekali jalan, waitUntilCompleted() memudahkan pemeriksaan serah terima: fungsi ini memblokir hingga buffer perintah dan handler penyelesaiannya selesai. Dalam renderer interaktif, hindari menempatkan penantian yang memblokir ini di setiap frame. Sebagai gantinya, pertahankan beberapa pasangan buffer dan tekstur, lalu kembalikan tiap pasangan ke pool yang tersedia hanya setelah menerima notifikasi penyelesaian GPU. Apple menunjukkan pola pool resource ini dalam contoh sinkronisasinya. waitUntilCompleted(), Synchronizing CPU and GPU work.
Jika nantinya GPU menulis melalui satu alias dan membaca melalui alias lain, tinjau kembali sinkronisasinya. Apple mencatat bahwa data tekstur yang didukung buffer dapat koheren antar-pass render tanpa langsung mencerminkan perubahan alias di dalam satu pass. Contoh ini menghindari kerumitan tersebut dengan menyelesaikan penggambaran CPU sebelum satu operasi baca GPU-nya. Buffer-backed texture coherence.
Diagnosis masalah tata letak sebelum mengubah renderer
Gunakan urutan berikut untuk mengisolasi kontrak yang gagal. Pemeriksaan ini mengikuti aturan tata letak piksel, resource pendukung, stride, dan sinkronisasi yang didokumentasikan di atas.
- Pembuatan tekstur gagal: pastikan GPU mendukung tekstur bersama, kedua resource menggunakan
.shared, serta offset dan stride memenuhi alignment yang ditanyakan. Pertahankan tekstur 2D, single-sample, dengan satu tingkat mip dan satu elemen array. - Merah tampak biru: periksa byte satu per satu. Contoh ini mensyaratkan urutan byte RGBA,
premultipliedLast,byteOrder32Big, dan.rgba8Unormsecara bersamaan. - Baris tampak miring: bandingkan stride konteks dan tekstur. Keduanya harus menggunakan
rowBytes, termasuk padding. - Gambar tampak terbalik: periksa transformasi gambar dan pemetaan koordinat tekstur renderer. Contoh ini menggunakan konteks gambar yang tidak dibalik. Argumen
flippedmilik AppKit menjelaskan status terbalik awal; argumen tersebut tidak menyusun ulang baris yang tersimpan. - Pembaruan tampak tidak konsisten: periksa apakah penggambaran CPU bertumpang tindih dengan akses GPU ke alokasi yang sama. Gunakan pelacakan penyelesaian sebelum menggunakannya kembali.
Batasan pembuatan tekstur berasal dari dokumentasi tekstur linear Apple, sedangkan perbedaan status terbalik berasal dari inisialisasi AppKit. Tangani orientasi secara eksplisit saat menambahkan shader tampilan sendiri. Linear-texture requirements, AppKit flipped-state parameter.
Setelah buffer pendukung, tata letak byte, stride berpadded, dan jadwal akses selaras, gambar sintetis tersedia bagi Metal melalui tekstur bersama. Pertahankan kontrak tersebut saat mengubah ukuran gambar atau menambahkan operasi penggambaran; pilih salinan staging jika GPU target atau kebutuhan tekstur berikutnya mengharuskan penyimpanan terpisah.
Sumber
MTLTexture — Apple
MTLStorageMode.shared — Apple
contents() — Apple
Graphics Contexts — Apple
Bitmap Images and Image Masks — Apple
CGBitmapInfo — Apple
MTLPixelFormat.rgba8Unorm — Apple
linearSRGB — Apple
init(data:width:height:bitsPerComponent:bytesPerRow:space:bitmapInfo:) — Apple
init(cgContext:flipped:) — Apple
saveGraphicsState() — Apple
restoreGraphicsState() — Apple
buffer — Apple
Synchronizing CPU and GPU work — Apple
waitUntilCompleted() — Apple
MTLBlitCommandEncoder — Apple