diff --git a/CHANGELOG.md b/CHANGELOG.md index a0c9b006..b1abbf25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - **Breaking:** Removed `DamageOutOfRange` error case. If the damage value is greater than the backend supports, it is instead clamped to an appropriate value. - **Breaking:** Removed `SurfaceExtWeb` and the associated `NoDisplayHandle` and `NoWindowHandle` helpers. Use `RawWindowHandle::WebCanvas` or `RawWindowHandle::WebOffscreenCanvas` instead. - Fixed `present_with_damage` with bounds out of range on Windows, Web and X11. +- Reduced flickering when presenting while resizing on macOS. # 0.4.8 diff --git a/src/backends/cg.rs b/src/backends/cg.rs index 789dd442..d1a744bb 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -418,14 +418,30 @@ impl BufferInterface for BufferImpl<'_> { } .unwrap(); - // Wrap layer modifications in a transaction. Unclear if we should keep doing this, see - // for discussion about this. - CATransaction::begin(); + // Wrap things in a transaction if the event loop isn't currently running. + // + // The event loop implicitly commits pending layer updates at the end of the current run + // loop iteration, as documented in: + // + // + // + // + // 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. + let use_transaction = objc2_core_foundation::CFRunLoop::current() + .unwrap() + .current_mode() + .is_none(); + + if use_transaction { + CATransaction::begin(); + } // SAFETY: The contents is `CGImage`, which is a valid class for `contents`. unsafe { layer.setContents(Some(image.as_ref())) }; - CATransaction::commit(); + if use_transaction { + CATransaction::commit(); + } Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 69b4e0e6..2bdc3cf0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -352,6 +352,21 @@ impl Buffer<'_> { /// /// If the caller wishes to synchronize other surface/window changes, such requests must be sent to the /// Wayland compositor before calling this function. + /// + /// ## macOS / iOS + /// + /// On macOS/iOS/etc., this sets the [contents] of the underlying [`CALayer`], but doesn't yet + /// actually commit those contents to the compositor; that is instead done automatically by + /// QuartzCore at the end of the current iteration of the runloop. This synchronizes the + /// contents with the rest of the window, which is important to avoid flickering when resizing. + /// + /// If you need to send the contents to the compositor immediately (might be useful when + /// rendering from a separate thread or when using Softbuffer without the standard AppKit/UIKit + /// runloop), you'll want to wrap this function in a [`CATransaction`]. + /// + /// [contents]: https://developer.apple.com/documentation/quartzcore/calayer/contents?language=objc + /// [`CALayer`]: https://developer.apple.com/documentation/quartzcore/calayer?language=objc + /// [`CATransaction`]: https://developer.apple.com/documentation/quartzcore/catransaction?language=objc #[inline] pub fn present(self) -> Result<(), SoftBufferError> { // Damage the entire buffer.