-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Inspector v2: Add lightweight runtime "instrumentation" helpers and a usage example #16748
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
Inspector v2: Add lightweight runtime "instrumentation" helpers and a usage example #16748
Conversation
Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s). |
Snapshot stored with reference name: Test environment: To test a playground add it to the URL, for example: https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/16748/merge/index.html#WGZLGJ#4600 Links to test babylon tools with this snapshot: https://playground.babylonjs.com/?snapshot=refs/pull/16748/merge To test the snapshot in the playground with a playground ID add it after the snapshot query string: https://playground.babylonjs.com/?snapshot=refs/pull/16748/merge#BCU1XR#0 |
WebGL2 visualization test reporter: |
Visualization tests for WebGPU |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM but lets be sure we are not hooking everything all the time.
I guess It should for instance never be on when looking at the scene stats.
Yes that's exactly how I was testing actually. :) |
… usage example (BabylonJS#16748) One of the problems we currently have in tooling (and especially in inspector v1) is having the UI react to scene state changes. Ideally we'd have observables for every mutable state of a scene, but the runtime overhead would be too high to make this practical. We currently work around this in one of a few ways: 1. In some of the tools, we use a global observable, where any part of the tool UI that mutates any scene state is supposed to fire the observable. This way all the different parts of the tool UI can communicate state changes that they initiate. 2. When state changes don't come from the tools (e.g. Playground, or really any app that uses Inspector), we just poll for state changes (e.g. continuously re-render react components every ~500ms for example to detect changes). 3. Provide a refresh button, where the user must click the button to see changes. This PR introduces a new idea, which I refer to as "lightweight runtime instrumentation." The idea is to temporarily hook function calls and property setters to produce a transient observable. This would not scale if we tried to hook every property on every object of the scene simultaneously, but in reality only a very small part of the scene state is bound to the inspector UI at any time, e.g.: 1. The properties of a single object (in the properties pane). 2. The names of as many nodes as fit into scene explorer (since it virtualizes, so there are not that many live react components for tree view nodes bound to scene entities). When a different entity is bound to the properties pane, or the scene explorer is scrolled, the temporary hooks are removed and the entity objects are restored to their original state. With this in mind, hooking functions/properties to observe scene state changes specifically for tooling should be quite low overhead, so this PR introduces the idea and has one example of the usage. There are helper functions for "intercepting" function calls and property setters, and then a React hook that translates this to React state, e.g.: `const computeBonesUsingShadersObservable = useInterceptObservable("property", mesh, "computeBonesUsingShaders")` This observable (and the temporary instrumentation of the `computeBonesUsingShaders` property) is reverted as soon as the consuming react component is unmounted. Take a look in this order: 1. The `useObservableState` in [meshGeneralProperties.tsx](https://github.com/BabylonJS/Babylon.js/pull/16748/files#diff-dca74d1795e7e366439f24e66a2a26fe0b14ef5f3fc4ceb3b05585033b781955R10) - this is just showing an existing concept where given an observable, we can produce React state that the component can use to re-render. 2. The `useInterceptObservable` used along with `useObservableState` in [meshAdvancedProperties.tsx](https://github.com/BabylonJS/Babylon.js/pull/16748/files#diff-1fc176dd99343ae0ff259bf520c6504813ab7c02fbbb06292280310d7a2e588dR11) - this shows using the new `useInterceptObservable` to create a temporary observable that lets us immediately respond to state changes of a property that doesn't actually have an observable. 4. The `useObservableState` implementation in [instrumentationHooks.ts](https://github.com/BabylonJS/Babylon.js/pull/16748/files#diff-92b79d9fbaaf8f9eef52315919e1ae5640553f1262ca2c43618f091ebb29de4dR19). 5. The `InterceptFunction` helper function in [functionInstrumentation.ts](https://github.com/BabylonJS/Babylon.js/pull/16748/files#diff-0124bc224339ce336c700bf1128d94268f9abcc1ad92cebeb171e825e250d07cR20). 6. The `InterceptProperty` helper function in [propertyInstrumentation.ts](https://github.com/BabylonJS/Babylon.js/pull/16748/files#diff-ec2332f5239be4ddf8eab5cb2faa5bb1cf36da855b214cbc44a25f43981a6496R20). Open to feedback on this idea. So far it seems like it works quite well.
One of the problems we currently have in tooling (and especially in inspector v1) is having the UI react to scene state changes. Ideally we'd have observables for every mutable state of a scene, but the runtime overhead would be too high to make this practical. We currently work around this in one of a few ways:
This PR introduces a new idea, which I refer to as "lightweight runtime instrumentation." The idea is to temporarily hook function calls and property setters to produce a transient observable. This would not scale if we tried to hook every property on every object of the scene simultaneously, but in reality only a very small part of the scene state is bound to the inspector UI at any time, e.g.:
When a different entity is bound to the properties pane, or the scene explorer is scrolled, the temporary hooks are removed and the entity objects are restored to their original state.
With this in mind, hooking functions/properties to observe scene state changes specifically for tooling should be quite low overhead, so this PR introduces the idea and has one example of the usage. There are helper functions for "intercepting" function calls and property setters, and then a React hook that translates this to React state, e.g.:
const computeBonesUsingShadersObservable = useInterceptObservable("property", mesh, "computeBonesUsingShaders")
This observable (and the temporary instrumentation of the
computeBonesUsingShaders
property) is reverted as soon as the consuming react component is unmounted.Take a look in this order:
useObservableState
in meshGeneralProperties.tsx - this is just showing an existing concept where given an observable, we can produce React state that the component can use to re-render.useInterceptObservable
used along withuseObservableState
in meshAdvancedProperties.tsx - this shows using the newuseInterceptObservable
to create a temporary observable that lets us immediately respond to state changes of a property that doesn't actually have an observable.useObservableState
implementation in instrumentationHooks.ts.InterceptFunction
helper function in functionInstrumentation.ts.InterceptProperty
helper function in propertyInstrumentation.ts.Open to feedback on this idea. So far it seems like it works quite well.