Skip to content

Conversation

@madsmtm
Copy link
Member

@madsmtm madsmtm commented Jan 31, 2026

Make Buffer<'_> be backed by a shared memory buffer that can be shared directly with the compositor. This makes the CoreGraphics backend zero-copy (QuartzCore was copying from the CGImageProvider internally before when committing the transaction).

This also implements triple buffering of the surface, to avoid creating a bunch of unnecessary buffers. The compositor seems to want to work on two buffers at the same time? A bit unsure why, but I know that we do get tearing if we try to touch things while it's working on it.

Fixes #83.

This requires #321, because IOSurface does not support RGBX (so the alpha channel must be opaque to render correctly).

Tested on:

  • macOS 15.7.3 M2
  • macOS 10.12.6 Intel
  • iOS simulator

Replaces #95 and #96.

@madsmtm madsmtm added enhancement New feature or request DS - CoreGraphics macOS/iOS/tvOS/watchOS/visionOS backend labels Jan 31, 2026
Comment on lines +301 to +317
// Block until back buffer is no longer being used by the compositor.
//
// TODO: Allow configuring this: https://github.com/rust-windowing/softbuffer/issues/29
// TODO: Is this actually the check we want to do? It seems like the compositor doesn't
// properly set the usage state when the application loses focus, even if you continue
// rendering there?
//
// Should we instead set up a `CVDisplayLink`, and only allow using the back buffer once a
// certain number of frames have passed since it was presented? Would be better though not
// perfect, `CVDisplayLink` isn't guaranteed to actually match the display's refresh rate:
// https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/CoreVideo/CVProg_Concepts/CVProg_Concepts.html#//apple_ref/doc/uid/TP40001536-CH202-DontLinkElementID_2
//
// Another option would be to keep a boundless queue as described in:
// https://github.com/commercial-emacs/commercial-emacs/blob/68f5a28a316ea0c553d4274ce86e95fc4a5a701a/src/nsterm.m#L10552-L10571
while self.back.surface.is_in_use() {
std::thread::yield_now();
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we wait for the compositor to stop using the buffer(s) here is kinda bad, but that can be resolved later, and shouldn't be a problem for applications that either:

  1. Only re-render when something changes.
  2. Do proper frame-pacing (Winit doesn't yet provide that though).

Write directly into a shared memory buffer that can be shared directly
with the compositor and avoids copying bytes when presenting (QuartzCore
was copying stuff before).

This also implements double and triple buffering of the surface, to
avoid creating a bunch of unnecessary buffers. The compositor seems to
sometimes work on two buffers at the same time? A bit unsure why.

The way we wait for the compositor to stop using the buffer(s) is kinda
bad, but that can be resolved later, and shouldn't be a problem for
applications that do proper frame-pacing (which Winit doesn't yet
provide though).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DS - CoreGraphics macOS/iOS/tvOS/watchOS/visionOS backend enhancement New feature or request

Development

Successfully merging this pull request may close these issues.

Investigate more optimal way to implement CoreGraphics backend

2 participants