diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eb2b664..9a05df4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased +- Added `Surface::set_write_only()` for optimizing for write-only workloads. - Added `Buffer::pixels()` for accessing the buffer's pixel data. - Added `Buffer::pixel_rows()` for iterating over rows of the buffer data. - Added `Buffer::pixels_iter()` for iterating over each pixel with its associated `x`/`y` coordinate. diff --git a/src/backend_dispatch.rs b/src/backend_dispatch.rs index f2a94540..c3744cd5 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -108,6 +108,15 @@ macro_rules! make_dispatch { } } + fn set_write_only(&mut self, write_only: bool) { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.set_write_only(write_only), + )* + } + } + fn buffer_mut(&mut self) -> Result, SoftBufferError> { match self { $( diff --git a/src/backend_interface.rs b/src/backend_interface.rs index 1eafbf82..b93f978e 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -26,6 +26,11 @@ pub(crate) trait SurfaceInterface &W; /// Resize the internal buffer to the given width and height. fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError>; + + fn set_write_only(&mut self, _write_only: bool) { + // No-op by default. + } + /// Get a mutable reference to the buffer. fn buffer_mut(&mut self) -> Result, SoftBufferError>; /// Fetch the buffer from the window. diff --git a/src/lib.rs b/src/lib.rs index d54d2b9d..43d5cc4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,47 @@ impl Surface { self.surface_impl.resize(width, height) } + /// Set the buffer as optimized for only being written to. + /// + /// Setting this allows the underlying storage to bypass certain caches and reduce cache + /// pollution. In turn, this may make reading from the buffer data perform very poorly. + /// + /// As such, when rendering with this set, you should make sure to set pixels in their entirety: + /// + /// ``` + /// # let pixel = &mut 0x00ffffff; + /// # let (blue, green, red) = (0x11, 0x22, 0x33); + /// *pixel = blue | (green << 8) | (red << 16); + /// # assert_eq!(*pixel, 0x00332211); + /// ``` + /// + /// Instead of e.g. something like: + /// + /// ``` + /// # let pixel = &mut 0x00ffffff; + /// # let (blue, green, red) = (0x11, 0x22, 0x33); + /// // DISCOURAGED! + /// *pixel &= 0x00000000; // Clear + /// *pixel |= blue; // Set blue pixel + /// *pixel |= green << 8; // Set green pixel + /// *pixel |= red << 16; // Set red pixel + /// # assert_eq!(*pixel, 0x00332211); + /// ``` + /// + /// This is disabled by default. + /// + /// # Platform-specific + /// + /// This isn't yet implemented on any platforms, and is simply a no-op. + /// + /// On macOS, this may in the future set `kIOSurfaceCacheMode` to + /// `kIOSurfaceMapWriteCombineCache`. + #[inline] + // TODO: Add `write_only` getter? Would it ever really be useful? + pub fn set_write_only(&mut self, write_only: bool) { + self.surface_impl.set_write_only(write_only) + } + /// Copies the window contents into a buffer. /// /// ## Platform Dependent Behavior @@ -175,33 +216,9 @@ impl HasWindowHandle for Surface /// /// # Reading buffer data /// -/// Reading from buffer data may perform very poorly, as the underlying storage of zero-copy -/// buffers, where implemented, may set options optimized for CPU writes, that allows them to bypass -/// certain caches and avoid cache pollution. -/// -/// As such, when rendering, you should always set the pixel in its entirety: -/// -/// ``` -/// # let pixel = &mut 0x00ffffff; -/// # let (blue, green, red) = (0x11, 0x22, 0x33); -/// *pixel = blue | (green << 8) | (red << 16); -/// # assert_eq!(*pixel, 0x00332211); -/// ``` -/// -/// Instead of e.g. something like: -/// -/// ``` -/// # let pixel = &mut 0x00ffffff; -/// # let (blue, green, red) = (0x11, 0x22, 0x33); -/// // DISCOURAGED! -/// *pixel &= 0x00000000; // Clear -/// *pixel |= blue; // Set blue pixel -/// *pixel |= green << 8; // Set green pixel -/// *pixel |= red << 16; // Set red pixel -/// # assert_eq!(*pixel, 0x00332211); -/// ``` -/// -/// To discourage reading from the buffer, `&self -> &[u8]` methods are intentionally not provided. +/// The API of this is simplified for writing to buffer data, so various `&self -> &[X]` methods are +/// intentionally not provided. You can still read from the buffer data via. the `&mut self` methods +/// though. /// /// # Data representation ///