You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Move frame encoding to separate thread when possible (#829)
## Proposed Changes If no CATransaction sync is needed, perform Picture recorded commands execution on a separate thread. Fix a freeze on `waitUntilScheduled` if no transaction is available by moving synchronization from interop scope (any UIView is present in the composition) to per-frame scope (any UIView transaction is issued by composition in current frame). ## Testing Test: N/A ## Issues Fixed Removes work from main thread, when possible, allowing Compose to run there without waiting for CPU to finish encoding GPU commands. A freeze on `waitUntilScheduled` during interop synchronization if no transaction is available. <img width="324" alt="Screenshot 2023-09-19 at 12 13 00" src="https://github.com/JetBrains/compose-multiplatform-core/assets/4167681/e54d83e3-ca58-43aa-a4a7-4764dd8ce841"> <img width="371" alt="Screenshot 2023-09-19 at 12 13 05" src="https://github.com/JetBrains/compose-multiplatform-core/assets/4167681/8e4fcf0b-2b64-473e-a930-fae97a086a12"> ## Depends on #820
// We only need to synchronize this specific frame if there are any pending changes or isForcedToPresentWithTransactionEveryFrame is true
369
-
val synchronizePresentation = isForcedToPresentWithTransactionEveryFrame || (presentsWithTransaction && interopTransaction.isNotEmpty())
384
+
val mustEncodeAndPresentOnMainThread = presentsWithTransaction || waitUntilCompletion
370
385
371
-
val commandBuffer = queue.commandBuffer()!!
372
-
commandBuffer.label ="Present"
386
+
val encodeAndPresentBlock = {
387
+
surface.canvas.drawPicture(picture)
388
+
picture.close()
389
+
surface.flushAndSubmit()
373
390
374
-
if (!synchronizePresentation) {
375
-
// If there are no pending changes in UIKit interop, present the drawable ASAP
376
-
commandBuffer.presentDrawable(metalDrawable)
377
-
}
391
+
val commandBuffer = queue.commandBuffer()!!
392
+
commandBuffer.label ="Present"
378
393
379
-
commandBuffer.addCompletedHandler {
380
-
// Signal work finish, allow a new command buffer to be scheduled
381
-
dispatch_semaphore_signal(inflightSemaphore)
382
-
}
383
-
commandBuffer.commit()
384
-
385
-
if (synchronizePresentation) {
386
-
// If there are pending changes in UIKit interop, [waitUntilScheduled](https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443036-waituntilscheduled) is called
387
-
// to ensure that transaction is available
388
-
commandBuffer.waitUntilScheduled()
389
-
metalDrawable.present()
390
-
interopTransaction.actions.fastForEach {
391
-
it.invoke()
394
+
if (!presentsWithTransaction) {
395
+
commandBuffer.presentDrawable(metalDrawable)
392
396
}
393
397
394
-
if (interopTransaction.state ==UIKitInteropState.ENDED) {
395
-
isInteropActive =false
398
+
commandBuffer.addCompletedHandler {
399
+
// Signal work finish, allow a new command buffer to be scheduled
400
+
dispatch_semaphore_signal(inflightSemaphore)
396
401
}
402
+
commandBuffer.commit()
397
403
398
-
CATransaction.commit()
399
-
}
404
+
if (presentsWithTransaction) {
405
+
// If there are pending changes in UIKit interop, [waitUntilScheduled](https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443036-waituntilscheduled) is called
406
+
// to ensure that transaction is available
407
+
commandBuffer.waitUntilScheduled()
408
+
metalDrawable.present()
400
409
401
-
surface.close()
402
-
renderTarget.close()
403
-
// TODO manually release metalDrawable when K/N API arrives
410
+
interopTransaction.actions.fastForEach {
411
+
it.invoke()
412
+
}
404
413
405
-
// Track current inflight command buffers to synchronously wait for their schedule in case app goes background
406
-
if (inflightCommandBuffers.size == metalLayer.maximumDrawableCount.toInt()) {
407
-
inflightCommandBuffers.removeAt(0)
408
-
}
414
+
if (interopTransaction.state ==UIKitInteropState.ENDED) {
415
+
isInteropActive =false
416
+
}
417
+
}
418
+
419
+
surface.close()
420
+
renderTarget.close()
409
421
410
-
inflightCommandBuffers.add(commandBuffer)
422
+
// Track current inflight command buffers to synchronously wait for their schedule in case app goes background
0 commit comments