slint icon indicating copy to clipboard operation
slint copied to clipboard

swrenderer: Window background change don't mark the whole area as dirty for partial rendering

Open KortanZ opened this issue 9 months ago • 13 comments

I'm using ESP32C3 and following the Official MCU guide to build a demo. It works but i encounter two problems.

My Cargo.toml slint part

[dependencies.slint]
version = "1.5.1"
default-features = false
features = ["compat-1-2", "unsafe-single-threaded", "libm", "renderer-software"]
[build-dependencies]
slint-build = "1.5.1"

My .slint file

import { VerticalBox, Switch } from "std-widgets.slint";
export component Demo inherits Window{
    width: 128px;
    height: 128px;
    in-out property<bool> check: false;
    background: sw.checked ? #db073d:#07dba5;
    VerticalBox {
        alignment: center;
        Text {
            text: "Bg color test";
            font-size: 15px;
            horizontal-alignment: center;
        }
        sw := Switch {
            checked: check;
            text: "Switch";
        }
        Slider {
            minimum: 0;
            value: sw.checked ? 0 : 100;
            maximum: 100;
        }
    }
}

I'm using linebuffer method with super loop approach by following the guide.

  1. Can't use Slider widget, when add Slider in my .slint, compiler will error out with:

    called Result::unwrap() on an Err value: CompileError(["/home/kortan/Documents/code/rust/esp32/slint/ui/main.slint:21: Unknown type Slider"])

  2. When toggled switch, not full background freshed, only switch area changed.

    https://github.com/slint-ui/slint/assets/12783634/9ceffc3a-a483-4240-8a08-949f6900917b

    And when config animate to background, display get even weird.

import { VerticalBox, Switch } from "std-widgets.slint";
export component Demo inherits Window{
 width: 128px;
 height: 128px;
 in-out property<bool> check: false;
 background: sw.checked ? #db073d:#07dba5;
 animate background {
      duration: 300ms;
 }
 VerticalBox {
     alignment: center;
     Text {
         text: "Bg color test";
         font-size: 15px;
         horizontal-alignment: center;
     }
     sw := Switch {
         checked: check;
         text: "Switch";
     }
 }
}

https://github.com/slint-ui/slint/assets/12783634/bf587c83-a24f-455d-ae07-76ee24a3602f

KortanZ avatar May 12 '24 02:05 KortanZ

Can't use Slider widget, when add Slider in my .slint, compiler will error out with:

Did you import the Slider? Adding it into the import {...} should work.

When toggled switch, not full background freshed, only switch area changed.

That sounds like a bug to me...

hunger avatar May 13 '24 08:05 hunger

Did you import the Slider? Adding it into the import {...} should work.

Just realize that i've made a really dumb question. Sorry for that.

That sounds like a bug to me...

The slint code works well on slintpad. So there may be something wrong inside draw_if_needed.

KortanZ avatar May 13 '24 09:05 KortanZ

After update to slint 1.6.0, things are not getting better much.

https://github.com/slint-ui/slint/assets/12783634/1cde03bc-f676-496a-8625-c8fea856240c

KortanZ avatar May 14 '24 16:05 KortanZ

How are you rendering to the screen? We made a change in 1.6 regarding the dirty regions. Render now returns the region that has multiple rectangles, all of which need to be flushed to the screen. Do you use the return value of render() somewhere?

tronical avatar May 14 '24 17:05 tronical

@tronical

How are you rendering to the screen?

My whole project code is here, everything about slint is in ui.rs, the render loop looks like this:

    loop {
        slint::platform::update_timers_and_animations();
        window.draw_if_needed(|renderer| {
            renderer.render_by_line(DisplayWrapper{
                display: &mut display,
                line_buffer: &mut line_buffer
            });
        });

        if !window.has_active_animations() {
            if let Some(duration) = slint::platform::duration_until_next_timer_update() {
                Timer::after(Duration::from_millis(duration.as_millis() as u64)).await;
                continue;
            }
        }
        Timer::after(Duration::from_millis(10)).await;
    }

and the LineBufferProvider implemention is like:

struct DisplayWrapper<'a, T>{
    display: &'a mut T,
    line_buffer: &'a mut [slint::platform::software_renderer::Rgb565Pixel],
}

impl<T: DrawTarget<Color = Rgb565>>
    slint::platform::software_renderer::LineBufferProvider for DisplayWrapper<'_, T>
{
    type TargetPixel = slint::platform::software_renderer::Rgb565Pixel;
    fn process_line(
        &mut self,
        line: usize,
        range: core::ops::Range<usize>,
        render_fn: impl FnOnce(&mut [Self::TargetPixel]),
    ) {
        // Render into the line
        render_fn(&mut self.line_buffer[range.clone()]);

        self.display.fill_contiguous(
            &Rectangle::new(Point::new(range.start as _, line as _), Size::new(range.len() as _, 1)),
            self.line_buffer[range.clone()].iter().map(|p| RawU16::new(p.0).into())
        ).map_err(drop).unwrap();
    }
}

Do you use the return value of render() somewhere?

No, I'm using render_by_line() instead and not using return value at all.

KortanZ avatar May 14 '24 17:05 KortanZ

Thanks for confirming. So it can’t be that. Does the background change work if you remove the animation on the background property? (Not a solution of course, but helps narrow down a theory I have)

tronical avatar May 14 '24 19:05 tronical

@tronical

Does the background change work if you remove the animation on the background property?

https://github.com/slint-ui/slint/assets/12783634/6eddea0a-02bb-438e-859b-45bdd7504d38

Just like when using slint 1.5.1 as i shown on top of this issue, the only difference between animated version and no animation version is that no animation version could change the color correctly rather than a unexpected deeper or lighter color.

KortanZ avatar May 15 '24 00:05 KortanZ

Ouch, ok, so my theory of it being related to the time is not true either :(. Since you're calling render_by_line, could you try to see via debug output what the values are for the line: usize parameter? When toggling the switch, you should get the function invoked also with line values that start at the top of the screen (so zero and onwards).

tronical avatar May 15 '24 07:05 tronical

@tronical I've added debug output in process_line, and line: usize was start from 70 and end at 82 when toggling the switch. So it seems that only color changed area have been re-rendered.

KortanZ avatar May 15 '24 13:05 KortanZ

The problem here is in the Slint implementation of the partial rendering for the software renderer. The change in the Window background color is not making any area dirty.

As a workaround, you can use a top level Rectangle with a background.

ogoffart avatar May 24 '24 08:05 ogoffart

@ogoffart Thank you for your solution : ). I think there are two problem now. One is dirty marking that you mentioned, and the other one is animate problem. As shown in video clip, animation not work as expected either. When configured animate, color dosen't animate but only show a darker or lighter color instead.

KortanZ avatar May 24 '24 08:05 KortanZ

I think this is the same problem: the background color is not being tracked, so even if it animates, it doesn't mark anything dirty and so nothing gets repainted. Only other part of the UI that changes get repainted.

ogoffart avatar May 24 '24 10:05 ogoffart

@ogoffart

I think this is the same problem: the background color is not being tracked, so even if it animates, it doesn't mark anything dirty and so nothing gets repainted. Only other part of the UI that changes get repainted.

You are right, i've tested your workaround, and animation works well too.

KortanZ avatar May 24 '24 13:05 KortanZ