mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-31 15:47:26 +00:00
esp-idf: Add support for line-by-line rendering
Rendering by line into a line buffer that's in IRAM can be faster than accessing framebuffers in slower PSRAM, so offer this by allowing users to omit even the initial framebuffer.
This commit is contained in:
parent
fb2bbd8d92
commit
042bde7883
4 changed files with 201 additions and 36 deletions
|
@ -15,15 +15,19 @@
|
|||
* - `size` is the size of the screen
|
||||
* - `panel` is a handle to the display.
|
||||
* - `touch` is a handle to the touch screen, if the device has a touch screen
|
||||
* - `buffer1` is a buffer of at least the size of the frame in which the slint scene will be drawn.
|
||||
* Slint will take care to flush it to the screen
|
||||
* - `buffer1`, if specified, is a buffer of at least the size of the frame in which the slint scene
|
||||
* will be drawn. Slint will take care to flush it to the screen
|
||||
* - `buffer2`, if specified, is a second buffer to be used with double buffering,
|
||||
* both buffer1 and buffer2 should then be obtained with `esp_lcd_rgb_panel_get_frame_buffer`
|
||||
* - `rotation` applies a transformation while rendering in the buffer
|
||||
*
|
||||
* If no buffer1 is specified, Slint assumes that no direct framebuffers are accessible and instead
|
||||
* will render line-by-line, by allocating a line buffer with MALLOC_CAP_INTERNAL, and flush it to
|
||||
* the screen with esp_lcd_panel_draw_bitmap.
|
||||
*/
|
||||
void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
|
||||
std::optional<esp_lcd_touch_handle_t> touch,
|
||||
std::span<slint::platform::Rgb565Pixel> buffer1,
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer1 = {},
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer2 = {}
|
||||
#ifdef SLINT_FEATURE_EXPERIMENTAL
|
||||
,
|
||||
|
|
|
@ -22,7 +22,7 @@ struct EspPlatform : public slint::platform::Platform
|
|||
{
|
||||
EspPlatform(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
|
||||
std::optional<esp_lcd_touch_handle_t> touch,
|
||||
std::span<slint::platform::Rgb565Pixel> buffer1,
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer1,
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer2 = {}
|
||||
#ifdef SLINT_FEATURE_EXPERIMENTAL
|
||||
,
|
||||
|
@ -52,7 +52,7 @@ private:
|
|||
slint::PhysicalSize size;
|
||||
esp_lcd_panel_handle_t panel_handle;
|
||||
std::optional<esp_lcd_touch_handle_t> touch_handle;
|
||||
std::span<slint::platform::Rgb565Pixel> buffer1;
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer1;
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer2;
|
||||
#ifdef SLINT_FEATURE_EXPERIMENTAL
|
||||
slint::platform::SoftwareRenderer::RenderingRotation rotation;
|
||||
|
@ -214,16 +214,20 @@ void EspPlatform::run_event_loop()
|
|||
}
|
||||
|
||||
if (std::exchange(m_window->needs_redraw, false)) {
|
||||
if (buffer1) {
|
||||
auto buffer1 = *this->buffer1;
|
||||
auto rotated = false
|
||||
#ifdef SLINT_FEATURE_EXPERIMENTAL
|
||||
|| rotation
|
||||
== slint::platform::SoftwareRenderer::RenderingRotation::Rotate90
|
||||
== slint::platform::SoftwareRenderer::RenderingRotation::
|
||||
Rotate90
|
||||
|| rotation
|
||||
== slint::platform::SoftwareRenderer::RenderingRotation::Rotate270
|
||||
== slint::platform::SoftwareRenderer::RenderingRotation::
|
||||
Rotate270
|
||||
#endif
|
||||
;
|
||||
auto region =
|
||||
m_window->m_renderer.render(buffer1, rotated ? size.height : size.width);
|
||||
auto region = m_window->m_renderer.render(buffer1,
|
||||
rotated ? size.height : size.width);
|
||||
auto o = region.bounding_box_origin();
|
||||
auto s = region.bounding_box_size();
|
||||
if (s.width > 0 && s.height > 0) {
|
||||
|
@ -233,25 +237,49 @@ void EspPlatform::run_event_loop()
|
|||
xSemaphoreTake(sem_vsync_end, portMAX_DELAY);
|
||||
#endif
|
||||
|
||||
// Assuming that using double buffer means that the buffer comes from the
|
||||
// driver and we need to pass the exact pointer.
|
||||
// Assuming that using double buffer means that the buffer comes from
|
||||
// the driver and we need to pass the exact pointer.
|
||||
// https://github.com/espressif/esp-idf/blob/53ff7d43dbff642d831a937b066ea0735a6aca24/components/esp_lcd/src/esp_lcd_panel_rgb.c#L681
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, size.width, size.height,
|
||||
buffer1.data());
|
||||
|
||||
std::swap(buffer1, buffer2.value());
|
||||
} else {
|
||||
for (int y = o.y; y < o.y + s.height; y++) {
|
||||
for (int x = o.x; x < o.x + s.width; x++) {
|
||||
// Swap endianess to big endian
|
||||
auto px =
|
||||
reinterpret_cast<uint16_t *>(&buffer1[y * size.width + x]);
|
||||
auto px = reinterpret_cast<uint16_t *>(
|
||||
&buffer1[y * size.width + x]);
|
||||
*px = (*px << 8) | (*px >> 8);
|
||||
}
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, o.x, y, o.x + s.width, y + 1,
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, o.x, y, o.x + s.width,
|
||||
y + 1,
|
||||
buffer1.data() + y * size.width + o.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
slint::platform::Rgb565Pixel *lb =
|
||||
(slint::platform::Rgb565Pixel *)heap_caps_malloc(
|
||||
size.width * 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
|
||||
m_window->m_renderer.render_by_line([this,
|
||||
&lb](std::size_t line_y,
|
||||
std::size_t line_start,
|
||||
std::size_t line_end,
|
||||
void (*render_fn)(
|
||||
void *,
|
||||
std::span<slint::platform::
|
||||
Rgb565Pixel>
|
||||
&),
|
||||
void *render_fn_data) {
|
||||
std::span<slint::platform::Rgb565Pixel> view { lb, line_end - line_start };
|
||||
render_fn(render_fn_data, view);
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, line_start, line_y, line_end,
|
||||
line_y + 1, lb);
|
||||
});
|
||||
free(lb);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_window->window().has_active_animations()) {
|
||||
|
@ -292,7 +320,7 @@ TaskHandle_t EspPlatform::task = {};
|
|||
|
||||
void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
|
||||
std::optional<esp_lcd_touch_handle_t> touch,
|
||||
std::span<slint::platform::Rgb565Pixel> buffer1,
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer1,
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer2
|
||||
#ifdef SLINT_FEATURE_EXPERIMENTAL
|
||||
,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "slint.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
struct xcb_connection_t;
|
||||
|
@ -630,6 +631,55 @@ public:
|
|||
return PhysicalRegion { r };
|
||||
}
|
||||
|
||||
/// Render the window scene, line by line. The provided Callback will be invoked for each line
|
||||
/// that needs to rendered.
|
||||
///
|
||||
/// The renderer uses a cache internally and will only render the part of the window
|
||||
/// which are dirty.
|
||||
///
|
||||
/// This function returns the physical region that was rendered considering the rotation.
|
||||
///
|
||||
/// The callback is invoked with the line number as first parameter, and the start x and end x
|
||||
/// coordinates of the line as second and third parameter. The implementation must provide a
|
||||
/// line buffer (as std::span) and invoke the provided fourth function pointer (render_fn) with
|
||||
/// it, to fill it with pixels. The last parameter of Callback is a private data pointer that
|
||||
/// must be provided as first argument to render_fn.
|
||||
/// After the line buffer is filled with pixels, your implementation is free to flush that line
|
||||
/// to the screen for display.
|
||||
template<std::invocable<std::size_t, std::size_t, std::size_t,
|
||||
void (*)(void *, std::span<Rgb565Pixel> &), void *>
|
||||
Callback>
|
||||
PhysicalRegion render_by_line(Callback process_line_callback) const
|
||||
{
|
||||
auto r = cbindgen_private::slint_software_renderer_render_by_line_rgb565(
|
||||
inner,
|
||||
[](void *process_line_callback_ptr, uintptr_t line, uintptr_t line_start,
|
||||
uintptr_t line_end, void (*render_fn)(const void *, uint16_t *, std::size_t),
|
||||
const void *render_fn_data) {
|
||||
struct RenderFnAndData
|
||||
{
|
||||
void (*render_fn)(const void *, uint16_t *, std::size_t);
|
||||
const void *render_fn_data;
|
||||
};
|
||||
RenderFnAndData rfad;
|
||||
rfad.render_fn = render_fn;
|
||||
rfad.render_fn_data = render_fn_data;
|
||||
|
||||
(*reinterpret_cast<Callback *>(process_line_callback_ptr))(
|
||||
std::size_t(line), std::size_t(line_start), std::size_t(line_end),
|
||||
[](void *rfad_ptr, std::span<Rgb565Pixel> &line_span) {
|
||||
RenderFnAndData *rfad =
|
||||
reinterpret_cast<RenderFnAndData *>(rfad_ptr);
|
||||
rfad->render_fn(rfad->render_fn_data,
|
||||
reinterpret_cast<uint16_t *>(line_span.data()),
|
||||
line_span.size());
|
||||
},
|
||||
&rfad);
|
||||
},
|
||||
&process_line_callback);
|
||||
return PhysicalRegion { r };
|
||||
}
|
||||
|
||||
# ifdef SLINT_FEATURE_EXPERIMENTAL
|
||||
/// This enum describes the rotation that is applied to the buffer when rendering.
|
||||
/// To be used in set_rendering_rotation()
|
||||
|
|
|
@ -351,7 +351,9 @@ mod software_renderer {
|
|||
use super::*;
|
||||
type SoftwareRendererOpaque = *const c_void;
|
||||
use i_slint_core::graphics::{IntRect, Rgb8Pixel};
|
||||
use i_slint_core::software_renderer::{RepaintBufferType, Rgb565Pixel, SoftwareRenderer};
|
||||
use i_slint_core::software_renderer::{
|
||||
LineBufferProvider, RepaintBufferType, Rgb565Pixel, SoftwareRenderer,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn slint_software_renderer_new(
|
||||
|
@ -400,6 +402,87 @@ mod software_renderer {
|
|||
i_slint_core::graphics::euclid::rect(orig.x, orig.y, size.width as i32, size.height as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn slint_software_renderer_render_by_line_rgb565(
|
||||
r: SoftwareRendererOpaque,
|
||||
process_line_fn: extern "C" fn(
|
||||
*mut core::ffi::c_void,
|
||||
usize,
|
||||
usize,
|
||||
usize,
|
||||
extern "C" fn(*const core::ffi::c_void, *mut u16, usize),
|
||||
*const core::ffi::c_void,
|
||||
),
|
||||
user_data: *mut core::ffi::c_void,
|
||||
) -> IntRect {
|
||||
struct Rgb565Processor {
|
||||
process_line_fn: extern "C" fn(
|
||||
*mut core::ffi::c_void,
|
||||
usize,
|
||||
usize,
|
||||
usize,
|
||||
extern "C" fn(*const core::ffi::c_void, *mut u16, usize),
|
||||
*const core::ffi::c_void,
|
||||
),
|
||||
user_data: *mut core::ffi::c_void,
|
||||
}
|
||||
|
||||
impl LineBufferProvider for Rgb565Processor {
|
||||
type TargetPixel = Rgb565Pixel;
|
||||
fn process_line(
|
||||
&mut self,
|
||||
line: usize,
|
||||
range: core::ops::Range<usize>,
|
||||
render_fn: impl FnOnce(&mut [Rgb565Pixel]),
|
||||
) {
|
||||
self.cpp_process_line(line, range, render_fn);
|
||||
}
|
||||
}
|
||||
|
||||
impl Rgb565Processor {
|
||||
fn cpp_process_line<RenderFn: FnOnce(&mut [Rgb565Pixel])>(
|
||||
&mut self,
|
||||
line: usize,
|
||||
range: core::ops::Range<usize>,
|
||||
render_fn: RenderFn,
|
||||
) {
|
||||
let mut render_fn = Some(render_fn);
|
||||
let render_fn_ptr =
|
||||
&mut render_fn as *mut Option<RenderFn> as *const core::ffi::c_void;
|
||||
|
||||
extern "C" fn cpp_render_line_callback<RenderFn: FnOnce(&mut [Rgb565Pixel])>(
|
||||
render_fn_ptr: *const core::ffi::c_void,
|
||||
line_start: *mut u16,
|
||||
len: usize,
|
||||
) {
|
||||
let line_slice = unsafe {
|
||||
core::slice::from_raw_parts_mut(line_start as *mut Rgb565Pixel, len)
|
||||
};
|
||||
let render_fn =
|
||||
unsafe { (*(render_fn_ptr as *mut Option<RenderFn>)).take().unwrap() };
|
||||
render_fn(line_slice);
|
||||
}
|
||||
|
||||
(self.process_line_fn)(
|
||||
self.user_data,
|
||||
line,
|
||||
range.start,
|
||||
range.end,
|
||||
cpp_render_line_callback::<RenderFn>,
|
||||
render_fn_ptr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let renderer = &*(r as *const SoftwareRenderer);
|
||||
|
||||
let processor = Rgb565Processor { process_line_fn, user_data };
|
||||
|
||||
let r = renderer.render_by_line(processor);
|
||||
let (orig, size) = (r.bounding_box_origin(), r.bounding_box_size());
|
||||
i_slint_core::graphics::euclid::rect(orig.x, orig.y, size.width as i32, size.height as i32)
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental")]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn slint_software_renderer_set_rendering_rotation(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue