Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- **Breaking:** Disable the DRM/KMS backend by default.
- **Breaking:** Removed `DamageOutOfRange` error case. If the damage value is greater than the backend supports, it is instead clamped to an appropriate value.
- Fixed `present_with_damage` with bounds out of range on Windows, Web and X11.
- Reduced flickering when presenting while resizing on macOS.

# 0.4.7

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ objc2-foundation = { version = "0.3.2", default-features = false, features = [
"NSString",
"NSThread",
"NSValue",
"NSNull",
] }
objc2-quartz-core = { version = "0.3.2", default-features = false, features = [
"std",
Expand Down
24 changes: 14 additions & 10 deletions src/backends/cg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::backend_interface::*;
use crate::error::InitError;
use crate::{util, Pixel, Rect, SoftBufferError};
use objc2::rc::Retained;
use objc2::runtime::{AnyObject, Bool};
use objc2::runtime::{AnyObject, Bool, ProtocolObject};
use objc2::{define_class, msg_send, AllocAnyThread, DefinedClass, MainThreadMarker, Message};
use objc2_core_foundation::{CFRetained, CGPoint};
use objc2_core_graphics::{
Expand All @@ -12,10 +12,10 @@ use objc2_core_graphics::{
};
use objc2_foundation::{
ns_string, NSDictionary, NSKeyValueChangeKey, NSKeyValueChangeNewKey,
NSKeyValueObservingOptions, NSNumber, NSObject, NSObjectNSKeyValueObserverRegistration,
NSKeyValueObservingOptions, NSNull, NSNumber, NSObject, NSObjectNSKeyValueObserverRegistration,
NSString, NSValue,
};
use objc2_quartz_core::{kCAGravityTopLeft, CALayer, CATransaction};
use objc2_quartz_core::{kCAGravityTopLeft, CALayer};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle};

use std::ffi::c_void;
Expand Down Expand Up @@ -225,6 +225,17 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
// resized to something that doesn't fit, see #177.
layer.setContentsGravity(unsafe { kCAGravityTopLeft });

// The CALayer has a default action associated with a change in the layer contents, causing
// a quarter second fade transition to happen every time a new buffer is applied.
//
// We avoid this by setting the action for the "contents" key to NULL.
//
// TODO(madsmtm): Do we want to do the same for bounds/contentsScale for smoother resizing?
layer.setActions(Some(&NSDictionary::from_slices(
&[ns_string!("contents")],
&[ProtocolObject::from_ref(&*NSNull::null())],
)));

// Initialize color space here, to reduce work later on.
let color_space = CGColorSpace::new_device_rgb().unwrap();

Expand Down Expand Up @@ -354,16 +365,9 @@ impl BufferInterface for BufferImpl<'_> {
}
.unwrap();

// The CALayer has a default action associated with a change in the layer contents, causing
// a quarter second fade transition to happen every time a new buffer is applied. This can
// be avoided by wrapping the operation in a transaction and disabling all actions.
CATransaction::begin();
CATransaction::setDisableActions(true);

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

CATransaction::commit();
Ok(())
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,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.
Expand Down