Fix: Dispatch Metal completion handler to MainActor
Some checks are pending
Auto Tag on Push / auto-tag (push) Waiting to run

This commit is contained in:
ewen 2026-01-16 00:16:44 +01:00
parent fb1bf90d5a
commit 87e3d99290

View file

@ -105,7 +105,7 @@ final class MetalImageRenderer {
var params = params var params = params
if params.algorithm == 7, let pipe1 = pipelineStateFS_Pass1, let pipe2 = pipelineStateFS_Pass2 { if params.algorithm == 7, let pipe1 = pipelineStateFS_Pass1, let pipe2 = pipelineStateFS_Pass2 {
print("🔄 Using Floyd-Steinberg two-pass rendering") print("🔄 Using Floyd-Steinberg two-pass rendering")
// FLOYD-STEINBERG MULTI-PASS // FLOYD-STEINBERG MULTI-PASS
@ -170,32 +170,21 @@ final class MetalImageRenderer {
computeEncoder.endEncoding() computeEncoder.endEncoding()
// Add completion handler properly inside the closure // Add completion handler properly inside the closure
commandBuffer.addCompletedHandler { buffer in commandBuffer.addCompletedHandler { [weak self] buffer in
// We must jump back to MainActor if we want to do UI stuff, but here we just process data. // CRITICAL: Dispatch back to MainActor because self (MetalImageRenderer) is isolated
// However, continuation must be resumed. // and createCGImage is isolated to MainActor.
// Since the whole function is @MainActor, we should likely resume on main actor? Task { @MainActor in
// Actually, withCheckedContinuation handles the resume context automatically or acts as a bridge. guard let self = self else {
// But to be safe and strict, let's keep it simple. continuation.resume(returning: nil)
return
}
if let error = buffer.error { if let error = buffer.error {
print("❌ Metal command buffer error: \(error)") print("❌ Metal command buffer error: \(error)")
continuation.resume(returning: nil) continuation.resume(returning: nil)
} else { } else {
print("✅ Metal render completed successfully") print("✅ Metal render completed successfully")
// Texture -> CGImage conversion is fast enough to do here or dispatch to main // Now we are on MainActor, we can safely call self.createCGImage
// But since createCGImage creates data copies, it is safe.
// We need the result.
DispatchQueue.main.async {
// We are back on main thread (required for MetalImageRenderer methods if isolated)
// But wait, makeCGImage is private and inside this class.
// If we call self.createCGImage here, we are inside a closure which is NOT isolated to MainActor by default unless specified.
// Let's call a helper or do it carefully.
// BETTER APPROACH:
// Just resume with the texture or nil, and do conversion after await?
// OR: perform conversion here.
// Since `createCGImage` is private and self is MainActor, we must be on MainActor to call it.
let result = self.createCGImage(from: outputTexture) let result = self.createCGImage(from: outputTexture)
if result == nil { if result == nil {
print("❌ Failed to create CGImage from output texture") print("❌ Failed to create CGImage from output texture")