Thank you all for your posts. I took some time to think at what I should add right now, I considered expanding on this or that concept, but I won't as that would probably end up killing the discussion.
What I did in those hours was experimenting with the implementation I had produced and it's ability to scale. I still cannot trust it so I went back here and to MollyRocket's forums. I only had a quick glance at them previously, but with my code getting grosser and grosser, I couldn't just walk along. I originally hoped to at least be able to communicate my decisions, but I realize this is just impossible, as I'm still very confused. I will try to pour in some thoughts with some degree of separation.
Use multi-frame GUI!
So far, I've tried my best to work without the single-frame lag. I didn't do this because of the time lapse involved, I just didn't consider it useful. However, as I previously noted, I'll need to buffer the various draw requests anyway to guarantee some minimal performance scalability - with at least one drawcall per widget, things would have turned slow FAST - so right now alot of this machinery is already in place. Long story short, I was wrong and
didn't correctly evaluate benefits and drawbacks.
I was thinking about rewriting towards a dual-frame GUI however, as you'll read below, I am now considering bite the bullet and go RMGUI instead.
Maybe finding nv-widget wasn't good
I was very impressed with this library because with a simple implementation and no IDs to generate everything felt like a dream.
However, I overlooked the fact that as
icastano notes: "
The NVIDIA IMGUI does not use IDs, but instead it requires widgets to stay at the same screen location to retain the focus. It's not ideal, but works fine for our purposes and keeps the code simple". I'm not truly sure of what he meant and I'll save you from the (probably useless) flow of thoughts that resulted but I now think the library made me overly optimistic. I am quite on the opposite line of thinking now, as I'm not even sure to be able to break this.
By the way, while at this I just want to link to
Zero Memory Widget, maybe someone will be able to see the light.
ID generation: not for the faint of heart
I stopped reading at about half 2006 and I suddenly figured out that after all, given the amount of messages in four years, maybe IMGUI just isn't as well understood as I originally believed. To be completely honest, I now suspect the paradigm likely didn't deliver as expected. I can understand that many people like me will have discussed the trouble in their forums of choice instead of on MollyRocket's but it seems there just have been not so much traffic, isn't it?
In particular state retaining requires some kind of ID-ing which must be automatic - I agree with MoundS when he says that it's too much of a burden to place on library's user - and it turns out that ID generation is still being discussed.
Even worse than that, I'm now a bit confused about auto IDing. In general, state retaining seems to be VERY complicated.
Current line of thinking
Today I will redesign to reach a few different goals. First of all, IMGUIs are not about efficiency but on ease of use. I see a parallel with scripting and I have some script-ish machinery which could probably support the problem with some slight modifications. So, right now, I foresee different IMGUI systems.
The IMGUI I had developed up to now will become the
native C++ IMGUI system. It will be nowhere as complex at originally anticipated. No complex widgets, little to no state retaining, no eyecandy, no theming.
Its only goal is to reach sufficient sophistication to support components on top of it and even this is now open to debate. I hope to never use this directly, if not in the initial stages.
I then considered a
managed, script-assisted IMGUI system. This is quite more lengthy so it's discussed below.
The third system would be a
managed, script assisted RMGUI. I hoped that I could delay this to the far future but with more and more doubts arising I am considering just biting the bullet. As I've started to think seriously at this only a few hours ago, I'm not even sure how far I want to go with this. Sadly, the very same problems of dynamic IMGUIs apply there so how to link the systems (GUI<->application) is not quite clear to me besides having callbacks. Years ago I used GLUI, which allowed to pour data directly in application's varables and it looked quite nice to me. I thought that maybe reflection could somehow help. Say for example that I pass a structure with all the variables I need to fetch and the system figures out the layout himself... It's a very blurred picture for the time being.
Managed, script only IMGUI
I considered this to be my goal for a couple of days as it promised enough functionality to be a robust backbone. Ideally, the managed IMGUI system will be the preferred way of doing things for a long time. Being based on scripts as I said, I can jump thuru a few hoops, and one of that is automatic ID generation. I was thinking at automatically generated IDs like
{ thisRef callOff { groupIndex? appResPtr? }}
where
- thisRef is the "this" pointer from which the doControl call originated - who created that control and owns it.
- callOff comes from VM magic and it's doControl CALL instruction's offset in the "code segment". It is, for all purposes a sub-line call identification, __LINE__ on steroids, where the control was created. This still doesn't quite work in loops, nor in recursive things, that's why I have
- { groupIndex?, appResPtr? }: to discriminate controls born in the very same call, I've considered either a sequential group ID or storage id. Neither seems to work to me.
The group index would be set at 0 with a BeginGroup call and essentially count the widgets in the group until a corresponding EndGroup call is issued.
Let me build an example. Suppose I want to model a shop in which I can mark the items (say swords and shields) to buy. Say the list of swords and shields can change, even during interaction based on some metric. Suppose I draw some kind of themed checkbox which draws the object, a green tick and pulsates the background a-la win7 (so the "pulse" is the button-specific, uninteresting state). When done, the user goes checkout.
I'd like to just do:
...for(int i = 0; i < sword.length; i++) doCheckBox(buySword, ...); // because I have no choice but saving that myselffor(int i = 0; i < shield.length; i++) doCheckBox(buyShield, ...);if(doButton("Check out")) CheckOut(sword, buySword, shield, buyShield);...
Stuff like this will break {thisRef, callOff} big way as it cannot figure out the loop. With a sequential index, I could make it work anyway: if I can find a {thisRef, callOff} pair then I check the other parameters. There's a big issue here. If I want to hash by slot result, then I'm effectively asking the memory to stay persistent. This is all wrong.
Now, suppose in frame N I have 5 swords and 3 shields. In the following frames, I mark buySword[3] and buyShield[0]. Say it takes us to frame M.
At frame M+1 sword[3] becomes unavailable (say some designer wanted the sword to be available only at 20:00, with time going on while interacting. The very same code will be executed and what happens is that
- If I am ID-ing on storage, then I'm ... in trouble. There's no way the app can mark the state as invalid. For first, it would admit there's state. What will happen is that GUI-internal state will be moved to the wrong button. The buy tick will move to the button originally associated with (frame N) buySword[4].
This is more generally a very troublesome issue: no matter how smart the ID generation algorithm is, I just cannot see how it could deal with data shuffling, and this is a big problem.
Hashing on "source data pointer" (as opposed to destination) just does not feel right, it would imply for example that I cannot change a button's text description without invalidating its state. That's a no-go either.
- If I'm IDing on group index then I'm free of reallocating the buyXXX arrays which seems way more reasonable, but in this scenario, the very same thing happens.
RMGUIs do not solve the above issues, but it seems there's light at the end of the tunnel: they say since the beginning
there will be state to manage.
In "ID Generation and data lifetime" (link above) tmadden notes in a very well thought message that "The key [for dynamic IMGUIs] is that IDs don't identify individual widgets, they identify entire trees of static code.", he proposed a way to deal with this involving an anchor and a dynamic_block. I still have to fully digest his message, but it looks it's just the object-oriented version (admittedly correct and exception-safe) than my BeginGroup and EndGroup calls.
Nonetheless, I feel like requiring the machinery he's suggesting coul be so complex one could just go for a standard RMGUI at this point!
So, summing up, even making up my mind and rethinking my goals, I am now very confused.
I seriously appreciate what burnhand wrote, it essentially builds down to "keep it real". The issue is that I cannot even tell if the examples I make up in my mind do have sense, if the GUI I have in mind can work. The fantasy shop example above will break pretty much everything I can think at.
This is terrible.