GPU'da görüntü işleme için Swift ve Metal kullanan macOS projesi üzerinde çalışıyorum. Geçtiğimiz hafta yeni 15 inç MacBook Pro'yu (geç 2016) aldım ve kodumda tuhaf bir şey fark ettim: bir dokuya yazması gereken çekirdekler öyle görünmüyordu ...Metal çekirdekler yeni MacBook Pro'da düzgün çalışmıyor (geç 2016) GPU'lar
Çok sonra kazma, problemin, hesaplama yapmak için hangi GPU'nun Metal (AMD Radeon Pro 455 veya Intel (HD) 530 Grafik 5) tarafından kullanıldığıyla ilişkili olduğunu buldum.
Radeon ve Intel GPU'larda (MTLCreateSystemDefaultDevice()
Radeon varsayılan aygıtı döndürür iken) temsil eden cihazların bir dizisi MTLDevice
kullanılarak MTLCopyAllDevices()
döndürür başlatılması. Her durumda, kod Intel GPU ile beklendiği gibi çalışır, ancak Radeon GPU ile durum böyle değildir.
Size bir örnek göstereyim.
: Bu çekirdek kullanmak siparişkernel void passthrough(texture2d<uint, access::read> inTexture [[texture(0)]],
texture2d<uint, access::write> outTexture [[texture(1)]],
uint2 gid [[thread_position_in_grid]])
{
uint4 out = inTexture.read(gid);
outTexture.write(out, gid);
}
, ben bu kod parçası kullanın:
burada bir çıkış dokusuna bir giriş doku ve kopyalarını rengini alır basit bir çekirdek olduğunu başlatmak içinlet devices = MTLCopyAllDevices()
for device in devices {
print(device.name!) // [0] -> "AMD Radeon Pro 455", [1] -> "Intel(R) HD Graphics 530"
}
let device = devices[0]
let library = device.newDefaultLibrary()
let commandQueue = device.makeCommandQueue()
let passthroughKernelFunction = library!.makeFunction(name: "passthrough")
let cps = try! device.makeComputePipelineState(function: passthroughKernelFunction!)
let commandBuffer = commandQueue.makeCommandBuffer()
let commandEncoder = commandBuffer.makeComputeCommandEncoder()
commandEncoder.setComputePipelineState(cps)
// Texture setup
let width = 16
let height = 16
let byteCount = height*width*4
let bytesPerRow = width*4
let region = MTLRegionMake2D(0, 0, width, height)
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Uint, width: width, height: height, mipmapped: false)
// inTexture
var inData = [UInt8](repeating: 255, count: Int(byteCount))
let inTexture = device.makeTexture(descriptor: textureDescriptor)
inTexture.replace(region: region, mipmapLevel: 0, withBytes: &inData, bytesPerRow: bytesPerRow)
// outTexture
var outData = [UInt8](repeating: 128, count: Int(byteCount))
let outTexture = device.makeTexture(descriptor: textureDescriptor)
outTexture.replace(region: region, mipmapLevel: 0, withBytes: &outData, bytesPerRow: bytesPerRow)
commandEncoder.setTexture(inTexture, at: 0)
commandEncoder.setTexture(outTexture, at: 1)
commandEncoder.dispatchThreadgroups(MTLSize(width: 1,height: 1,depth: 1), threadsPerThreadgroup: MTLSize(width: width, height: height, depth: 1))
commandEncoder.endEncoding()
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
// Get the data back from the GPU
outTexture.getBytes(&outData, bytesPerRow: bytesPerRow, from: region , mipmapLevel: 0)
// Validation
// outData should be exactly the same as inData
for (i,outElement) in outData.enumerated() {
if outElement != inData[i] {
print("Dest: \(outElement) != Src: \(inData[i]) at \(i))")
}
}
Bu kodu let device = devices[0]
(Radeon GPU) ile çalıştırdığınızda outTexture hiçbir zaman (varsayım) yazılmaz ve sonuç olarak outData değişmeden kalır. Öte yandan, bu kodu let device = devices[1]
(Intel GPU) ile çalıştırırken, her şey beklendiği gibi çalışır ve outData, inData'daki değerler ile güncellenir.
Vay, bu eksik parçaydı, çok teşekkür ederim! Swift ve Metal'i geçtiğimiz birkaç ay boyunca paralel olarak öğrenmeye çalışıyorum ve kolay olduğunu söyleyemem. –