[WIP] calculate dirty region that needs to be redrawn
Context:
The intention of this PR isn't to be merged, but rather to get input/start a discussion for creating a PR which then could be merged.
I'd like to introduce the concept of a dirty region. This is the area that contains all the parts that need to be redrawn after the render step. Things outside this area wouldn't need to be redrawn.
I tried to keep this PR quite minimal, in hope that it shows the idea good enough, so that people can help me with finding out, what a proper implementation of this feature would look like (e.g. it should probably be a list of regions and not only a single one). Please help.
Background
The reason why I'd like to have this feature is, that I'd to use OrbTK to create UIs for the reMarkable, an e-ink device. You would render the full framebuffer, but then tell it to refresh only the parts that changed, hence the introduction of the dirty region.
I tried this PR with the culculator example and it successfully refreshes only the buttons if they are pressed.
I'd also like to thank you all for creating this library. Over the past few months (is it years already?) I looked into a good UI library that I could use for the reMarkable. I've tried out several ones, but the alternatives contain lots of abstractions which then leak through implementation details nonetheless . I like the approach OrbTK is currently taking, with rendering to a framebuffer, which is exactly what I need to get it working on a reMarkable. Thanks for switching to tiny-skia recently (which I've been experimenting with on the reMarkable as well), that made it really easy for me.
Hey @vmx welcome on board on the OrbTK planet. And thanks for takeing the courage to propose the region concept. If i got the idea right, you try to reduce the cycles needed to update the final UI output. The rendering backend will act as usual, since it will be called as needed without any changes ....
I have only taken a quick overview of your proposed PR code. So i might have failed to get the real benefit.
OrbTK is supporting the dirty property on all widgets. And it is my understanding, that it's aiming to support exactly your attitude to only update (measure and render) entities, that have been marked dirty. If a property changes (e.g. via state code), the parent entity is marked dirty.
As an example lets asume you change a popup:
- You change
visibilitystatus property it fromhiddentovisible. - The
measurefunction will need to calculate the new bounds for that entity. It was hidden, which means got a size of (0.0, 0.0). Beforehand It never influenced the measurement algo (in terms of bounds) and didn't get any rendering. Now the measurement will in term need to recalculate theboundsof that entity as well as all parent entities. - Next the
rendererwill step in and prepare the render buffer output, thus producing the byte-code that can be presented on the device. - All tasks will only act on the changed
dirtyentities.
@FloVanGH please correct me, if i haven't reproduced this process in the correct way.
My question: Where is the difference?
I can see that you porpose the ability to encounter a vector grouping all dirty entities in a predefined rectangle. Which might be useful in further processes?
My question: Where is the difference?
@rzerres As I understood the currently implemented dirty flag, it marks all entities that might be modified. This means you mark the element as well as all its parents. You then go through all of them and re-render them. A child might e.g. also change the size of a parent, hence also the parent is taken into account.
My code checks if an element actually changes visually, only then this area will be marked as dirty region. So if a child e.g. changes it's color, but not its size, then the parent won't be taken into account.
Though perhaps the dirty flag could be expanded to serve both use cases.
@vmx That makes sense.
It will result in reduced cpu load, reduced latency with faster reactivity time. And this is desired :)
I would prefer, if we can get away with improving the logic to only use the dirty property. Your approach will introduce two new properties to any entity object in the EntityComponentManager.
We might measure the improvement when changing theme, e.g from dark to light, where no font changes are involved, so no rendering is needed at all.
It will result in reduced cpu load, reduced latency with faster reactivity time. And this is desired :)
Theoretically yes, if you need to re-render less things, less CPU will be used. Though there's more work involved in order to determine whether re-rendering is needed or not.
I would prefer, if we can get away with improving the logic to only use the
dirtyproperty. Your approach will introduce two new properties to any entity object in the EntityComponentManager.
Re-using dirty should work as Option can hold the same information as a boolean. Though one new property (the one for keeping the previous render state), would still be needed. Or would you want to store both things in a struct in a single dirty property?
We might measure the improvement when changing
theme, e.g from dark to light, where no font changes are involved, so no rendering is needed at all.
A theme change from dark to light would change the color, hence a re-rendering would be needed (for all widgets).
Thinking about it again. I know too little about how the current "flag as dirty" process works. Doesn't it need to be a two step process as it currently is? First you flag all widgets as dirty which potentially changed and then in the second phase you determine which ones actually need to be re-rendered.
At the moment dirty is set to true if a property of a widget is changed. At this point also the changed handler are managed. I think with your mechanism the old dirty management can be obsolete.
Redox moved to GitLab years ago, thus if you still want to merge it, move this pull request to there.
If you still want to contribute to Redox, talk with us in the Matrix chat.