slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
17.64k stars 607 forks source link

Hardware acceleration for the software renderer. #5774

Open ogoffart opened 3 months ago

ogoffart commented 3 months ago

The software renderer customization point is currently the TargetPixel trait. I was thinking we could extend it be able to support hardware acceleration (SIMD, ST's DMA2D or NXP's PXP, or ESP's PPA). Note that these function might be async as they use different component that are not on the same CPU Another thing that can't be supported with the current API is buffer that have a stride in byte which is not a multiple of the pixel size. So we're considering a new API for SoftwareRenderer's render.

This is the result of a discussion with @tronical :

impl SoftwareRenderer {
    #[cfg(feature = "experimental")]
    pub async fn render_buffer(&self, buffer: impl TargetPixelBuffer)
}

#[cfg(feature = "experimental")]
pub trait TargetPixelBuffer {
    type Pixel: TargetPixel;
    fn line_slice(&self, line_numer: usize) -> &[Self::Pixel];

    //#[inline]
    //blend_pixel(&self, x: usize, y: usize, color: PremultipliedRgbaColor);

    async fn fill_rectangle(&self, rectangle: PhysicalRect, color: PremultipliedRgbaColor);
}

pub struct TargetPixelSlice<'a, T> {
    data: &'a mut [T],
    width: usize, // or stride?
}
impl<'a, T: TargetPixel> TargetPixelBuffer for TargetPixelSlice<'a, T>
{ ... }

#[cfg(feature = "experimental")]
pub use {PhysicalRect, PhysicalPx};

// also adapt ProcessScene to use it:
impl<'a, T: TargetPixel> ProcessScene for RenderToBuffer<'a, T> {
    fn process_rectangle(&mut self, geometry: PhysicalRect, color: PremultipliedRgbaColor) {        
        self.foreach_region(geometry, |rect| {
            TargetPixel::draw_rectangle(self.buffer, self.stride, rect, color)
        });
    }
}

That is only for blending a rectangle, but the same could be done for images and other operations that can be accelerated.

In our board-support stm32h735g-dk.rs

struct DMA2DBufferRgb565<'a> {
    data: &'a mut [Rgb565],
    width: usize, // or stride?
}
impl TargetPixelBuffer for DMA2DBufferRgb565 <'a, T> { ... }

and for C++ in slint-cpp:

struct CppAcceleratedBufferRgb565 {
    data: *mut Rgb565,
    width: usize,
    // function which we can forward to.
    draw_rectangle: fn(PhysicalRect, ....),
}

(which still need to be wrapped in a higher level API, maybe using coroutines)

leo60228 commented 3 months ago

If the renderer is being modified to use async, I think it could also be useful to also have an async version of render_by_line and LineBufferProvider.

scristall-bennu commented 1 month ago

Would love to help with this effort. We are evaluating slint on an STM32U5G9-dk in a C++ environment. I'm happy to implement the coroutine + DMA2D side, is there a test branch or proof-of-concept I can get started with?