entt icon indicating copy to clipboard operation
entt copied to clipboard

Ability to debug the composition of an Entity

Open samaursa opened this issue 2 years ago • 10 comments

More details here: https://github.com/skypjack/entt/issues/60#issuecomment-1667327628

It would be great to be able to debug an entity and all of it's components inside the debugger. In Visual Studio, with natvis support, the entity should be expandable in the watch window to show all the components that it has. Ideally, one would be able to expand the components as well to inspect their values.

samaursa avatar Aug 07 '23 17:08 samaursa

@skypjack is there any update on the trampoline class? I could help you out with the implementation (and natvis) if you get me started on what you have in mind with that class.

samaursa avatar Aug 22 '23 21:08 samaursa

Not yet, sorry. I'm back from vacation but also need to keep up with all other things before starting something new and tricky with natvis. 🙂 I'll ping you soon on this one though. During the last days, I tried to put a few ideas together for the pending issues. 👍

skypjack avatar Aug 23 '23 09:08 skypjack

I've spent some time on this during the last week and 🤷‍♂️ CustomListItems doesn't support a tree-like representation. I didn't expect it. 😞 Because of that, the only way to have something meaningful is to pass all types to the registry as in registry<T1, T2, ..., TN> or have a similar viewer with the type list as template arguments. This gets rid of a loop within natvis and makes it possible to use its container-like views to show the data for the entities. The other way around is to implement an imgui plugin and forget about natvis I guess... 😅

Unless you have some smart idea to do it, I think I'll close the issue since it's not implementable as I initially thought and I don't know how to solve it if they don't improve natvis itself a little. I'm sorry.

skypjack avatar Mar 01 '24 18:03 skypjack

@skypjack I do have an implementation for our internal purposes. It is not exactly fast (we can toggle it on/off at runtime) but it's not that slow either. It does the job and has been indispensable in debugging the entities.

The gist of it is:

  • we have a component called the Mapper which stores a pointer to all the components in an array
  • each time we add we also add the component pointer to the mapper
  • each time we remove we also remove the component pointer from the mapper
  • (not-so-great) we have a debug-only macro that we add to all our components to ensure pointer stability so that none of the components gets swapped out on us
  • the Mapper has a custom natvis definition that is able to display all the components of an Entity. We can even inspect the values of the component (and nested Entities in those components, if we have any).

I'm sure there are points of contention in the above such that it's not suitable to add to entt directly, especially if performance is sacrificed. However, I think it would be worth sacrificing performance (opt-in only) if a similar or better approach is provided by entt officially where entt has an optional (ideally switchable at runtime) 'debugging' mode which, while slower, provides the invaluable debugging capabilities that we are currently lacking in entt.

samaursa avatar Mar 04 '24 09:03 samaursa

In a project I work on, we used a slightly different approach (that could work in EnTT out of the box maybe).

First of all, note that natvis has an annoying idiosyncrasy. Usually, it shows the content of a derived class when there is a vtable. However, it gets crazy with primitive types and enums. In particular, a storage is shown from a pointer to a sparse set only if the entity type is a class rather than an enum. Don't ask me why, no one knows 😅 I can only imagine what the code is that runs behind natvis to get lost in such a thing. Anyway, this would be easily solved with an opt-in macro to use a class and would be completely transparent to the end user.

Once this is done, in the project I work on there is an entity object that also contains a pointer to a registry. This wouldn't be necessary in EnTT actually. A mixin or a debug only macro would be enough for the storage entity so that it can only use such an object internally. At this point, we would have a type T that contains an entity and a pointer to a registry (or sort of). There are no longer two loops, this type can have its own natvis view.

However, how would this work? The registry has pointers to sparse sets. Using a non-enum entity type would display the content of the derived class, however it would still not be accessible from natvis. Well, what we do in this project is this. For each entity (when expanded) the storages are iterated and those in which the entity isn't available are discarded. For the others, an item is added whose key is the position of the entity within the storage (which shows all its elements in order in the debugger, as if it were a vector). Therefore, the expansion doesn't directly show the component but a list where we know where to look. On the other hand, there is no need to enable any sort of risky tracking or to turn on pointer stability even where not required. Also, performance aren't affected in any case here.

I must say that I like it as a compromise and I find it easy to use at the end of the day. It gets the job done for sure. The next step to this I think is a plugin for VS or a gui imgui, the only two things I would use and therefore I would gladly mnaintain probably. 🙂

skypjack avatar Mar 04 '24 10:03 skypjack

That sounds great!

For each entity (when expanded) the storages are iterated and those in which the entity isn't available are discarded. For the others, an item is added whose key is the position of the entity within the storage (which shows all its elements in order in the debugger, as if it were a vector).

I am not a natvis wizard and I'm not sure how to iterate over the storages and sprinkle in some logic in the natvis (what is this now, a natvis script?) file. If you have any resource I can check out for this, I would appreciate it.

The next step to this I think is a plugin for VS or a gui imgui

How would the imgui gui debugger work without knowing the types at compile time? I assume some extra work would be required by the user [1]?

[1] this is one of the reasons we really liked our natvis approach, no extra work is required by the user to 'expose' the type in any way. It just works with the debugger.

samaursa avatar Mar 05 '24 02:03 samaursa

I am not a natvis wizard and I'm not sure how to iterate over the storages and sprinkle in some logic in the natvis (what is this now, a natvis script?) file. If you have any resource I can check out for this, I would appreciate it.

I think the best way to proceed is to implement what I've in mind for the handle class. It already owns a single entity and a reference to a registry after all. If it works and is a good compromise, we can then try to apply the same to the registry itself with the trick above.

How would the imgui gui debugger work without knowing the types at compile time? I assume some extra work would be required by the user [1]?

Oh, yeah, it would use meta or the like for sure. Right. I do something similar in a project of mine already, with a small macro to export component types for the imgui windows.

skypjack avatar Mar 06 '24 07:03 skypjack

@skypjack that sounds good

If this is to become a feature, I would request some modularity with the type information i.e. on the one hand one can use the macro you mentioned without any additional work. On the other hand, with minimal code, one should be able to leverage other reflection systems e.g. Unreal's type reflection, with the entt debugger.

samaursa avatar Mar 08 '24 20:03 samaursa

Ok, I added a view for the handle class to give you a grasp of what I mean and also have a starting point. Let me know if it makes sense to you or if it has obscure parts that I can improve/explain. Thanks. 👍

skypjack avatar Apr 02 '24 13:04 skypjack

Thanks @skypjack. I'll try it out asap.

samaursa avatar Apr 05 '24 03:04 samaursa