Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Rework item geometry #3115

Closed
wants to merge 15 commits into from
Closed

Conversation

Guiguiprim
Copy link
Contributor

@Guiguiprim Guiguiprim commented Jul 19, 2023

Closes #1932

I'm creating this PR to share where I'm at. But it is still buggy...

The following for example does not work:

import { HorizontalBox, GroupBox, TextEdit } from "std-widgets.slint";
export component App inherits Window {
  TextEdit {
    text: @tr("This is our TextEdit widget.");
    wrap: word-wrap;
    width: parent.width;
    height: parent.height;
    x: 0;
    y: 0;
  }
}

From a debug log using the interpreter after the flickable rework we have:

Output

            i-flickable-5 := Flickable {
               height: (i-scroll-view-4.height - 14px);
               interactive: false/*const*/;
               viewport-height: /../;
               viewport-x: 0px;
               viewport-y: 0px;
               width: (i-scroll-view-4.width - 14px);
               x: 2px/*const*/;
               y: 2px/*const*/;
               i-flickable-viewport-6 := Empty {
                  height: ;
                  height <=> i-flickable-5.viewport-height;
                  width: ;
                  width <=> i-flickable-5.viewport-width;
                  x: ;
                  x <=> i-flickable-5.viewport-x;
                  y: ;
                  y <=> i-flickable-5.viewport-y;
                  i-text-input-7 := TextInput {
                     color: if ((root-1.empty-2-state == 1)) { Palette-34.text-disabled } else { Palette-34.text-primary };
                     cursor-position-changed: /../;
                     edited: { root-1.empty-2-edited(i-text-input-7.text, ); };
                     enabled: true;
                     font-size: ((({ font-weight: (400/* as int */), font-size: 1.0766rem,  }/* as TextStyle */).font-size * GetWindowDefaultFontSize())/* as length */);
                     font-weight: ({ font-weight: (400/* as int */), font-size: 1.0766rem,  }/* as TextStyle */).font-weight/*const*/;
                     height: i-flickable-5.viewport-height;
                     read-only: false;
                     selection-background-color: (((4278221012/* as color */)/* as brush */)/* as color */)/*const*/;
                     selection-foreground-color: (i-text-input-7.color/* as color */);
                     single-line: false/*const*/;
                     text: Translate("This is our TextEdit widget.", "App", "", [], 1, "", );
                     text-cursor-width: 1px/*const*/;
                     width: i-flickable-5.viewport-width;
                     wrap: TextWrap.word-wrap;
                  }
               }
            }

And at the end:

Output

   i-text-input-7-preferred-height: ImplicitLayoutInfo(Vertical)((Weak), ).preferred;
   i-text-input-7-preferred-width: ImplicitLayoutInfo(Horizontal)((Weak), ).preferred;
   i-text-input-7-x: ((i-flickable-5.viewport-width - 0px) / 2);
   i-text-input-7-y: ((i-flickable-5.viewport-height - 0px) / 2);
   /.../
        i-flickable-5 := Flickable {
         interactive: false/*const*/;
         viewport-height: /../;
         viewport-x: 0px;
         viewport-y: 0px;
         i-flickable-viewport-6 := Empty {
            i-text-input-7 := TextInput {
               color: if ((root-1.empty-2-state == 1)) { Palette-34.text-disabled } else { Palette-34.text-primary };
               cursor-position-changed: /../;
               edited: { root-1.empty-2-edited(i-text-input-7.text, ); };
               enabled: true;
               font-size: ((({ font-size: 1.0766rem, font-weight: (400/* as int */),  }/* as TextStyle */).font-size * GetWindowDefaultFontSize())/* as length */);
               font-weight: ({ font-size: 1.0766rem, font-weight: (400/* as int */),  }/* as TextStyle */).font-weight/*const*/;
               read-only: false;
               selection-background-color: (((4278221012/* as color */)/* as brush */)/* as color */)/*const*/;
               selection-foreground-color: (i-text-input-7.color/* as color */);
               single-line: false/*const*/;
               text: Translate("This is our TextEdit widget.", "App", "", [], 1, "", );
               text-cursor-width: 1px/*const*/;
               wrap: TextWrap.word-wrap;
               geometry GeometryProps { x: root-1.i-text-input-7-x, y: root-1.i-text-input-7-y, width: i-text-input-7.width, height: i-text-input-7.height }
            }
            geometry GeometryProps { x: i-flickable-5.viewport-x, y: i-flickable-5.viewport-y, width: i-flickable-5.viewport-width, height: i-flickable-5.viewport-height }
         }
         geometry GeometryProps { x: root-1.i-flickable-5-x, y: root-1.i-flickable-5-y, width: root-1.empty-2-visible-width, height: root-1.empty-2-visible-height }
      }

Clearly things goes wrong somewhere:

  • i-text-input-7 somehow get center to the viewport
  • i-text-input-7 dimension are not set (should be bind to viewport)

So still some debugging to do, but I'm tacking a break.

@Guiguiprim
Copy link
Contributor Author

I know that i think about it, maybe trying to fix the tests could have help me find the issues... 😓

Copy link
Member

@ogoffart ogoffart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't look at details yet, but it looks good from a first glance

@@ -89,8 +90,9 @@ macro_rules! fn_render {
backend.draw_cached_pixmap(
item_rc,
&|callback| {
let width = self.width().get() * $dpr;
let height = self.height().get() * $dpr;
let geo = item_rc.geometry();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally here we should get the size from the call to render that has it instead of doing that.
(because each call to geometry add possibly 4 property dependency we don't need)

But we can leave that as an action item for later.

@Guiguiprim
Copy link
Contributor Author

Guiguiprim commented Jul 19, 2023

but it looks good from a first glance

Most commits are from you 😝

But thanks anyway.

@ogoffart
Copy link
Member

but it looks good from a first glance

Most commits are from you stuck_out_tongue_closed_eyes

Yes, I was referring to the commit you added.

@Guiguiprim
Copy link
Contributor Author

Guiguiprim commented Jul 19, 2023

OK so following breaking tests, I found an easier case to study (from slint::partial_render::simple):

export component Ui inherits Window {
    in property <color> c: yellow;
    background: black;
    Rectangle {
        x: 1phx;
        y: 80phx;
        width: 15phx;
        height: 17phx;
        background: red;
    }
    Rectangle {
        x: 10phx;
        y: 19phx;
        Rectangle {
            x: 5phx;
            y: 80phx;
            width: 12phx;
            height: 13phx;
            background: c;
        }
        Rectangle {
            x: 50phx;
            y: 8phx;
            width: 15phx;
            height: 17phx;
            background: c;
        }
    }
}

gives

Output

[internal/interpreter/dynamic_component.rs:771] &*component.root_element.borrow() = root-1 := WindowItem {
   property c;
   property rectangle-2-height;
   property rectangle-2-width;
   property rectangle-2-x;
   property rectangle-2-y;
   property rectangle-4-height;
   property rectangle-4-width;
   property rectangle-4-x;
   property rectangle-4-y;
   property rectangle-5-height;
   property rectangle-5-width;
   property rectangle-5-x;
   property rectangle-5-y;
   property x;
   property y;
   background: ((4278190080/* as color */)/* as brush */)/*const*/;
   c: (4294967040/* as color */)/*const*/;
   rectangle-2-height: (17phx / GetWindowScaleFactor());
   rectangle-2-width: (15phx / GetWindowScaleFactor());
   rectangle-2-x: (1phx / GetWindowScaleFactor());
   rectangle-2-y: (80phx / GetWindowScaleFactor());
   rectangle-4-height: (13phx / GetWindowScaleFactor());
   rectangle-4-width: (12phx / GetWindowScaleFactor());
   rectangle-4-x: (5phx / GetWindowScaleFactor());
   rectangle-4-y: (80phx / GetWindowScaleFactor());
   rectangle-5-height: (17phx / GetWindowScaleFactor());
   rectangle-5-width: (15phx / GetWindowScaleFactor());
   rectangle-5-x: (50phx / GetWindowScaleFactor());
   rectangle-5-y: (8phx / GetWindowScaleFactor());
   title: "Slint Window"/*const*/;
   rectangle-2 := Rectangle {
      background: ((4294901760/* as color */)/* as brush */)/*const*/;
      geometry GeometryProps { x: root-1.rectangle-2-x, y: root-1.rectangle-2-y, width: root-1.rectangle-2-width, height: root-1.rectangle-2-height }
   }
   rectangle-4 := Rectangle {
      background: (root-1.c/* as brush */);
      geometry GeometryProps { x: root-1.rectangle-4-x, y: root-1.rectangle-4-y, width: root-1.rectangle-4-width, height: root-1.rectangle-4-height }
   }
   rectangle-5 := Rectangle {
      background: (root-1.c/* as brush */);
      geometry GeometryProps { x: root-1.rectangle-5-x, y: root-1.rectangle-5-y, width: root-1.rectangle-5-width, height: root-1.rectangle-5-height }
   }
   geometry GeometryProps { x: root-1.x, y: root-1.y, width: root-1.width, height: root-1.height }
}

where we should have rectangle-4-x: (15phx / GetWindowScaleFactor());

And at some point with constant evaluation we should hope for something as simple as (in the spirit at least):

Output

[internal/interpreter/dynamic_component.rs:771] &*component.root_element.borrow() = root-1 := WindowItem {
   property c;
   property x;
   property y;
   background: ((4278190080/* as color */)/* as brush */)/*const*/;
   c: (4294967040/* as color */)/*const*/;
   title: "Slint Window"/*const*/;
   rectangle-2 := Rectangle {
      background: ((4294901760/* as color */)/* as brush */)/*const*/;
      geometry GeometryProps { x: (1phx / GetWindowScaleFactor()), y: (80phx / GetWindowScaleFactor()), width: (15phx / GetWindowScaleFactor()), height: (17phx / GetWindowScaleFactor()) }
   }
   rectangle-4 := Rectangle {
      background: (root-1.c/* as brush */);
      geometry GeometryProps { x: (15phx / GetWindowScaleFactor()), y: (99phx / GetWindowScaleFactor()), width: (12phx / GetWindowScaleFactor()), height: (13phx / GetWindowScaleFactor()) }
   }
   rectangle-5 := Rectangle {
      background: (root-1.c/* as brush */);
      geometry GeometryProps { x: (60phx / GetWindowScaleFactor()), y: (27phx / GetWindowScaleFactor()), width: (15phx / GetWindowScaleFactor()), height: (17phx / GetWindowScaleFactor()) }
   }
   geometry GeometryProps { x: root-1.x, y: root-1.y, width: root-1.width, height: root-1.height }
}

@ogoffart
Copy link
Member

Do you know if this was caused by one of my commit, or caused by your change?

@Guiguiprim
Copy link
Contributor Author

Do you know if this was caused by one of my commit, or caused by your change?

I'm only sure it was not cause by the first commit (I though is was a flickable issue at first).

I didn't try a bijection on other commits has I not sure it would even build/work.

@Guiguiprim
Copy link
Contributor Author

I guess we are missing a step needed by the addition of GeometryProp

@ogoffart
Copy link
Member

I see the problem: the optimize_useless_rectangles pass used to not optimize any rectangle that had a x and y property
But now that xand y are not on the rectangle, this means it is bing optimized.

So two options:

  1. check if the x and y geometry from that rectangle are unset or set to zero in the can_optimize
  2. Optimize these item anyway, but create a new property which is parent.x + x and put that property in the geometry

Option 1 is status qui as it used to be. But option 2 is great because it allow to optimize more now that we decouple the item geometry from their language properties.

@Guiguiprim
Copy link
Contributor Author

Guiguiprim commented Jul 19, 2023

It does look OK on your last commit:

[internal/interpreter/dynamic_component.rs:771] &*component.root_element.borrow() = root-1 := WindowItem {
   property<color> c;
   property<length> x;
   property<length> y;
   background: ((4278190080/* as color */)/* as brush */)/*const*/;
   c: (4294967040/* as color */)/*const*/;
   title: "Slint Window"/*const*/;
   rectangle-2 := Rectangle {
      background: ((4294901760/* as color */)/* as brush */)/*const*/;
      height: (17phx / GetWindowScaleFactor());
      width: (15phx / GetWindowScaleFactor());
      x: (1phx / GetWindowScaleFactor());
      y: (80phx / GetWindowScaleFactor());
   }
   rectangle-3 := Empty {
      height: root-1.height;
      width: root-1.width;
      x: (10phx / GetWindowScaleFactor());
      y: (19phx / GetWindowScaleFactor());
      rectangle-4 := Rectangle {
         background: (root-1.c/* as brush */);
         height: (13phx / GetWindowScaleFactor());
         width: (12phx / GetWindowScaleFactor());
         x: (5phx / GetWindowScaleFactor());
         y: (80phx / GetWindowScaleFactor());
      }
      rectangle-5 := Rectangle {
         background: (root-1.c/* as brush */);
         height: (17phx / GetWindowScaleFactor());
         width: (15phx / GetWindowScaleFactor());
         x: (50phx / GetWindowScaleFactor());
         y: (8phx / GetWindowScaleFactor());
      }
   }
}

EDIT: and more importantly the result is visibly OK

@Guiguiprim
Copy link
Contributor Author

By the way Empty does not have any properties left, so I was wondering about compiling it away, but I just checked there is still a CacheRenderingData in there so maybe not.

@ogoffart
Copy link
Member

By the way Empty does not have any properties left, so I was wondering about compiling it away, but I just checked there is still a CacheRenderingData in there so maybe not.

Ideally we can remove them all indeed. (in the optimize_useless_rectangles pass)

CacheRenderingData is another thing that was maybe not such a good idea to put there. and we may consider removing.

@Guiguiprim
Copy link
Contributor Author

To better get familiarize with the code I tried solution 1 first (I want to do solution 2), it fix the rectangle bug.

I will try to do solution 2 tomorrow.

The TextEdit bug is still there, so not done...

ogoffart and others added 14 commits July 24, 2023 17:55
Instead: add the viewport propety directly in the Flickable

Simplifies the compiler's code generation a bit
We need to account for the repeater count in the item sub-range
The WindowItem did not have a x or y before, so these property
were unused for it's geometry(), but now it is used in the new
item_geometry on the ComponentVTable.
So make sure we don't initialize them
I would love to add a clippy disallowed method for this, but it's a
generic type alias.
@Guiguiprim
Copy link
Contributor Author

I'm moving forward (almost done) for the solution 2 for fixing the rectangle optimization.

But at the time being, it ends up generating more properties, and I feel like more math optimization should be done afterward:

   rectangle-3-x: (10phx / GetWindowScaleFactor());
   rectangle-4-x: (5phx / GetWindowScaleFactor());
   rectangle-4-x-optimized-1: (root-1.rectangle-3-x + root-1.rectangle-4-x);

We could hope for only: rectangle-4-x-optimized-1: (15phx / GetWindowScaleFactor());

@ogoffart
Copy link
Member

I feel like more math optimization should be done afterward:

Unfortunately, we can't do inlining with the current expression_tree::Expression because property references to inner value are not representable.

That's why there is only const propagation pass and then a inline_expression pass that operate on the llr.

Of course there is room form improvements.

I have the feeling the use of "phx" is not so frequent, so this particular example might not be representative of real code.

But at the time being, it ends up generating more properties

Is it really more properties? Doesn't these new property used to b in a rectangle anyway?

@Guiguiprim
Copy link
Contributor Author

Guiguiprim commented Jul 25, 2023

Is it really more properties?

Not really, it's just more than it could be.

I have the feeling the use of "phx" is not so frequent, so this particular example might not be representative of real code.

I just use one of the test case, you are probably right

@Guiguiprim
Copy link
Contributor Author

because property references to inner value are not representable.

I'm not sure what you mean, but I trust you :p

@ogoffart
Copy link
Member

ogoffart commented Sep 12, 2023

Thanks for your work. I have taken your commit and put them in #3452

@ogoffart ogoffart closed this Sep 12, 2023
@Guiguiprim
Copy link
Contributor Author

Sorry for abandoning my PR, I wasn't able to make time to finish it.

Anyway glad to see that you managed to push it to the finish line !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactoring: remove geometry properties from the runtime Items
2 participants