Recommended way to know when a GUI control's properties are ready

Question
We are looking for a better way to know when a GUI control is ready to be rendered. When I say ready, I mean that all of the properties applied have been set and calculated.

Is there a recommended way of knowing when a GUI control is done updating?

Background
We are developing an application for a unique XR device. We have a GUI panel (advancedDynamicTexture mesh) that we are rendering in the scene at a very specific position. Its vital that the GUI be sized perfectly in the 3D scene.

The process we are currently using involves creating a fullscreen advancedDynamicTexture and a mesh advancedDynamicTexture. We add all of our controls to the fullscreen adt which uses a variety of control types and sizes itself. Once we think the controls have been calculated and ready to be rendered, we take the size of our root control and use its size to calculate the size of the mesh adt. This has been working quite well. Its allowing us to perfectly position the mesh adt in the scene without blocking raycasts in places where the texture was transparent.

We are finding that we are moving the controls to the mesh adt too early sometimes. For example, a recent panel we implemented appeared to be working well until we noticed that the text on a button was changing as soon as we interacted with the button on the mesh adt. After looking at the code for TextBlock we realized that setting the fontWeight marks the control as dirty and takes another cycle before it is rendered propertly.

We were having a similar problem with a GUI panel that used a ScrollViewer. We have an onAfterDrawObservable callback that we use to allow the ScrollViewer to shrink in height if it doesn’t need the full height of its parent. We were moving the control from the fullscreen adt to the mesh adt before that callback to adapt the ScollViewer’s height has occurred.

To solve that problem, we added a promise to let us know when the ScrollViewer’s height had been adapted and after that we moved the control to the mesh adt and it worked as expected.

That solution took quite a bit of effort and is somewhat brittle if the UI structure changes much. So we are hoping to find a better method for determining when we should move the control from the fullscreen adt to the mesh adt.

It seems that the isDirty property may be useful, but that also seems like an easy way to get into an infinite loop. I’m also not sure how the isDirty property works for child components. It would be preferred if we were able to identify that a control and its children are ready as opposed to keep track of every single control in the GUI.

Sorry, I don’t have a playground example to showcase, but if you need more context I will have our lead engineer jump in and answer the questions I cannot.

@carolhmj, Does this fall into your territory?

Thank you in advance!

1 Like

Hello! Thanks for the explanation of the context. Ultimately, the process of deciding when the GUI element has to be redrawn is a bit complex. One part involves the _layout function, which checks for isDirty, and also if the measures of the parent have changed in any way: https://github.com/BabylonJS/Babylon.js/blob/8329a78f996ce51cd787c19d13049c10f3a7d4ee/packages/dev/gui/src/2D/controls/control.ts#L1790. The process of rendering the ADT involves first doing the layout: https://github.com/BabylonJS/Babylon.js/blob/8329a78f996ce51cd787c19d13049c10f3a7d4ee/packages/dev/gui/src/2D/advancedDynamicTexture.ts#L760, then the render: https://github.com/BabylonJS/Babylon.js/blob/8329a78f996ce51cd787c19d13049c10f3a7d4ee/packages/dev/gui/src/2D/advancedDynamicTexture.ts#L785C33-L785C33. So changes in a parent control can cause changes in a child control, and vice versa.

There is a isReady function: https://github.com/BabylonJS/Babylon.js/blob/8329a78f996ce51cd787c19d13049c10f3a7d4ee/packages/dev/gui/src/2D/controls/control.ts#L2642, but that only checks if the control is ready to be rendered, not if the control has already been through the layout process (which is what it would change the width, height, etc).

Would it be possible in your scenario to take a slightly different approach and do this measuring process every GUI render instead of just once? Then, it wouldn’t matter if the control changes after the first replication, since the change would be replicated next render.

1 Like

Thanks @carolhmj, We are very open to considering other approaches to our problem.

If we were measuring on every GUI render, I believe that would require us to maintain two separate controls: one in screen space (who’s size is being monitored) and one in world space (who’s size is being updated).

We did try something similar to this at one point. We tried to use the control’s clone method to clone the entire control over to the world space adt. This seemed extremely promising, but after testing we realized the clone method didn’t work quite as expected. Specially we found that the control’s event handlers were lost during the clone, so our buttons didn’t have click handlers and the pointer animations were also lost. Ultimately we abandoned that idea.

Is there a different process you were envisioning?

Oh yeah, unfortunately that’s a limitation of how the clone method is implemented :frowning:

I hadn’t considered the matter of adding new controls to the GUI, just measuring what was already there. Would it be possible for you to describe a bit more about your setup?