Fix: Dispatch Metal completion handler to MainActor
Some checks are pending
Auto Tag on Push / auto-tag (push) Waiting to run
Some checks are pending
Auto Tag on Push / auto-tag (push) Waiting to run
This commit is contained in:
parent
fb1bf90d5a
commit
87e3d99290
|
|
@ -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")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue