Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89aab247 authored by Carlos Martinez Romero's avatar Carlos Martinez Romero
Browse files

libbufferstreams: Add Buffer, BufferPool and BufferOwner.

This change adds supoprt for a simple buffer pool. Buffer pools can be
notified when a buffer is dropped, so the buffer can be provided by the
pool again.

We introduced the concept of a BufferOwner, which is generic and can be
implemented by a client for their own custom buffer pools.

Along the way we updated the Frame struct to use a Buffer instead of a
AHardwareBuffer.

Pair: jshargo
Bug: 296450854, 296101127
Test: atest libbufferstreams-internal_test
Change-Id: Ib7c1ba19f96d1deb3d329366aa9215ad89228f9e
parent 53f8b579
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Wrapper around the HardwareBuffer

use nativewindow::*;

use super::{buffer_owner::NoBufferOwner, BufferOwner};

/// A wrapper for a hardware buffer.
///
/// This buffer may be associated with a buffer pool to which it will be returned to it when dropped.
pub struct Buffer {
    buffer_owner: Box<dyn BufferOwner>,
    hardware_buffer: HardwareBuffer,
}

impl Buffer {
    /// Create new buffer with a custom [BufferOwner].
    pub fn new(buffer_owner: Box<dyn BufferOwner>, hardware_buffer: HardwareBuffer) -> Self {
        Self { buffer_owner, hardware_buffer }
    }

    /// Create a new buffer with no association to any buffer pool.
    pub fn new_unowned(hardware_buffer: HardwareBuffer) -> Self {
        Self { buffer_owner: Box::new(NoBufferOwner), hardware_buffer }
    }

    /// Get the id of the underlying buffer.
    pub fn id(&self) -> u64 {
        self.hardware_buffer.id()
    }

    /// Get a reference to the underlying hardware buffer.
    pub fn buffer(&self) -> &HardwareBuffer {
        &self.hardware_buffer
    }
}

impl Drop for Buffer {
    fn drop(&mut self) {
        self.buffer_owner.on_return(self);
    }
}

#[cfg(test)]
mod test {
    use super::*;

    use crate::StreamConfig;

    const STREAM_CONFIG: StreamConfig = StreamConfig {
        width: 1,
        height: 1,
        layers: 1,
        format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
        usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
        stride: 0,
    };

    #[test]
    fn test_get_buffer_id() {
        let hardware_buffer = STREAM_CONFIG.create_hardware_buffer().unwrap();
        let buffer_id = hardware_buffer.id();

        let buffer = Buffer::new_unowned(hardware_buffer);
        assert_eq!(buffer_id, buffer.id());
    }
}
+28 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::Buffer;

/// Trait that represents an owner of a buffer that might need to handle events such as a buffer
/// being dropped.
pub trait BufferOwner {
    /// Called when a buffer is dropped.
    fn on_return(&self, buffer: &Buffer);
}

pub(super) struct NoBufferOwner;

impl BufferOwner for NoBufferOwner {
    fn on_return(&self, _buffer: &Buffer) {}
}
+137 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A Buffer Pool containing and managing HardwareBuffers

use std::{
    collections::HashMap,
    sync::{Arc, Mutex, Weak},
};

use nativewindow::*;

use crate::StreamConfig;

use super::{Buffer, BufferOwner};

pub(super) struct BufferPoolInner {
    size: usize,
    hardware_buffers: HashMap<u64, HardwareBuffer>,
    available_buffers: Vec<u64>,
}

impl BufferPoolInner {
    pub(super) fn return_buffer(&mut self, buffer_id: u64) {
        assert!(self.hardware_buffers.contains_key(&buffer_id));
        assert!(!self.available_buffers.contains(&buffer_id));

        self.available_buffers.push(buffer_id);
    }
}

struct BufferPoolOwner(Weak<Mutex<BufferPoolInner>>);

impl BufferOwner for BufferPoolOwner {
    fn on_return(&self, buffer: &Buffer) {
        if let Some(locked_buffer_pool) = self.0.upgrade() {
            let mut buffer_pool = locked_buffer_pool.lock().unwrap();

            buffer_pool.return_buffer(buffer.id());
        }
    }
}

/// A thread-safe collection of buffers.
///
/// A buffer pool can be of arbitrary size. It creates and then holds references to all buffers
/// associated with it.
pub struct BufferPool(Arc<Mutex<BufferPoolInner>>);

impl BufferPool {
    /// Creates a new buffer pool of size pool_size. All buffers will be created according to
    /// the stream config.
    ///
    /// This constructor creates all buffers at initialization.
    pub fn new(pool_size: usize, stream_config: StreamConfig) -> Option<Self> {
        let mut hardware_buffers = HashMap::new();
        let mut available_buffers = Vec::new();
        for _ in 0..pool_size {
            if let Some(buffer) = stream_config.create_hardware_buffer() {
                available_buffers.push(buffer.id());
                hardware_buffers.insert(buffer.id(), buffer);
            } else {
                return None;
            }
        }
        Some(Self(Arc::new(Mutex::new(BufferPoolInner {
            size: pool_size,
            hardware_buffers,
            available_buffers,
        }))))
    }

    /// Try to acquire the next available buffer in the buffer pool.
    ///
    /// If all buffers are in use it will return None.
    pub fn next_buffer(&mut self) -> Option<Buffer> {
        let mut inner = self.0.lock().unwrap();
        if let Some(buffer_id) = inner.available_buffers.pop() {
            Some(Buffer::new(
                Box::new(BufferPoolOwner(Arc::downgrade(&self.0))),
                inner.hardware_buffers[&buffer_id].clone(),
            ))
        } else {
            None
        }
    }

    /// Gets the size of the buffer pool.
    pub fn size(&self) -> usize {
        let inner = self.0.lock().unwrap();
        inner.size
    }
}

#[cfg(test)]
mod test {
    use super::*;

    const STREAM_CONFIG: StreamConfig = StreamConfig {
        width: 1,
        height: 1,
        layers: 1,
        format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
        usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
        stride: 0,
    };

    #[test]
    fn buffer_pool_next_buffer() {
        let mut buffer_pool = BufferPool::new(1, STREAM_CONFIG).unwrap();
        let next_buffer = buffer_pool.next_buffer();

        assert!(next_buffer.is_some());
        assert!(buffer_pool.next_buffer().is_none());
    }

    #[test]
    fn drop_buffer_returns_to_pool() {
        let mut buffer_pool = BufferPool::new(1, STREAM_CONFIG).unwrap();
        let next_buffer = buffer_pool.next_buffer();

        assert!(next_buffer.is_some());
        drop(next_buffer);
        assert!(buffer_pool.next_buffer().is_some());
    }
}
+23 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Module containing Buffers and BufferPools

mod buffer;
mod buffer_owner;
mod buffer_pool;

pub use buffer::*;
pub use buffer_owner::*;
pub use buffer_pool::*;
+11 −6
Original line number Diff line number Diff line
@@ -14,14 +14,15 @@

//! libbufferstreams: Reactive Streams for Graphics Buffers

pub mod buffers;
pub mod publishers;
mod stream_config;
pub mod subscribers;
pub mod subscriptions;

use buffers::Buffer;
pub use stream_config::*;

use nativewindow::*;
use std::time::Instant;

/// This function will print Hello World.
@@ -158,8 +159,8 @@ pub type BufferError = anyhow::Error;

/// Struct used to contain the buffer.
pub struct Frame {
    /// A handle to the C buffer interface.
    pub buffer: HardwareBuffer,
    /// A buffer to be used this frame.
    pub buffer: Buffer,
    /// The time at which the buffer was dispatched.
    pub present_time: Instant,
    /// A fence used for reading/writing safely.
@@ -172,6 +173,8 @@ mod test {
    use super::*;

    use anyhow::anyhow;
    use buffers::Buffer;
    use nativewindow::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
    use std::borrow::BorrowMut;
    use std::error::Error;
    use std::ops::Add;
@@ -192,9 +195,11 @@ mod test {

    fn make_frame() -> Frame {
        Frame {
            buffer: STREAM_CONFIG
            buffer: Buffer::new_unowned(
                STREAM_CONFIG
                    .create_hardware_buffer()
                    .expect("Unable to create hardware buffer for test"),
            ),
            present_time: Instant::now() + Duration::from_secs(1),
            fence: 0,
        }