Skip to content

Commit 380946c

Browse files
committed
Avoid the explicit CATransaction commit
Transactions are committed implicitly at the end of each run loop iteration, so if we know we're in a runloop, we'd rather use the later implicit commit. This effectively desyncs the surface from the rest of the window, which isn't desirable when resizing. Instead, we now commit in the implicit transaction that happens at the end of the current runloop iteration.
1 parent 66e0e2a commit 380946c

3 files changed

Lines changed: 36 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- **Breaking:** Removed `DamageOutOfRange` error case. If the damage value is greater than the backend supports, it is instead clamped to an appropriate value.
1818
- **Breaking:** Removed `SurfaceExtWeb` and the associated `NoDisplayHandle` and `NoWindowHandle` helpers. Use `RawWindowHandle::WebCanvas` or `RawWindowHandle::WebOffscreenCanvas` instead.
1919
- Fixed `present_with_damage` with bounds out of range on Windows, Web and X11.
20+
- Reduced flickering when presenting while resizing on macOS.
2021

2122
# 0.4.8
2223

src/backends/cg.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,14 +418,30 @@ impl BufferInterface for BufferImpl<'_> {
418418
}
419419
.unwrap();
420420

421-
// Wrap layer modifications in a transaction. Unclear if we should keep doing this, see
422-
// <https://github.com/rust-windowing/softbuffer/pull/275> for discussion about this.
423-
CATransaction::begin();
421+
// Wrap things in a transaction if the event loop isn't currently running.
422+
//
423+
// The event loop implicitly commits pending layer updates at the end of the current run
424+
// loop iteration, as documented in:
425+
// <https://developer.apple.com/documentation/quartzcore/catransaction?language=objc>
426+
//
427+
//
428+
//
429+
// This is a bit overly conservative; the full check on macOS could. But it doesn't matter _that_ much, if users decide to render in weird ways, we'll still support it when emitting a transaction, it'll just be slightly less efficient.
430+
let use_transaction = objc2_core_foundation::CFRunLoop::current()
431+
.unwrap()
432+
.current_mode()
433+
.is_none();
434+
435+
if use_transaction {
436+
CATransaction::begin();
437+
}
424438

425439
// SAFETY: The contents is `CGImage`, which is a valid class for `contents`.
426440
unsafe { layer.setContents(Some(image.as_ref())) };
427441

428-
CATransaction::commit();
442+
if use_transaction {
443+
CATransaction::commit();
444+
}
429445

430446
Ok(())
431447
}

src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,21 @@ impl Buffer<'_> {
352352
///
353353
/// If the caller wishes to synchronize other surface/window changes, such requests must be sent to the
354354
/// Wayland compositor before calling this function.
355+
///
356+
/// ## macOS / iOS
357+
///
358+
/// On macOS/iOS/etc., this sets the [contents] of the underlying [`CALayer`], but doesn't yet
359+
/// actually commit those contents to the compositor; that is instead done automatically by
360+
/// QuartzCore at the end of the current iteration of the runloop. This synchronizes the
361+
/// contents with the rest of the window, which is important to avoid flickering when resizing.
362+
///
363+
/// If you need to send the contents to the compositor immediately (might be useful when
364+
/// rendering from a separate thread or when using Softbuffer without the standard AppKit/UIKit
365+
/// runloop), you'll want to wrap this function in a [`CATransaction`].
366+
///
367+
/// [contents]: https://developer.apple.com/documentation/quartzcore/calayer/contents?language=objc
368+
/// [`CALayer`]: https://developer.apple.com/documentation/quartzcore/calayer?language=objc
369+
/// [`CATransaction`]: https://developer.apple.com/documentation/quartzcore/catransaction?language=objc
355370
#[inline]
356371
pub fn present(self) -> Result<(), SoftBufferError> {
357372
// Damage the entire buffer.

0 commit comments

Comments
 (0)