Hi Will, first of all, hopefully you could enjoy the holidays and have a good time. On 27.12.23 11:59, Will Godfrey wrote:
This is entirely a private matter between the core and the GUI. If something like LV2 wants to use a different interface it's up to that as to how it wants to interpret the data.
Indeed. The starting point (or larger picture) was that we were musing about how Yoshimi could cope with a situation where more restrictions are enforced regarding the operation of core and UI and their communication channels. Our aim is that all communication shall be passed through /some/ communication channel and protocol /irrespective how this protocol is actually implemented/. More specifically, in the current code base we have established some kind of communication system, which is anchored in InterChange and uses ringbuffers to exchange small command messages with low latency. What we're attempting in this discussion is to devise a structure which could use /any suitable communication system/ -- and in fact will use the existing communication system as a first step. And while doing so, we'll have to extend our communication system slightly. We both seem to agree that we'll have to extend it by some kind of data block passing mechanism. However, assuming there "is" such a communication channel, now the primary question is how to use it. What will be the communication pattern?
As I see it, we have a situation where the core can make a change to the data at any time regardless of whether it was triggered from the GUI or from somewhere else. Meanwhile, the GUI regularly loops at approximately 33mS. So I'd say that it's up to the GUI to find out if something has changed. I don't know how we can do that and then read the data without disturbing the core :(
In the preceding discussion, we have considered a pure "core push" and a pure "GUI pull" approach. Both have their benefits but also some drawbacks. The initial impression was that "GUI pull" might fit in better with the existing code, but this faces some problems, as the GUI must know reliably when to pull. This made me reconsider the Model-2 the "core push" again, since this model clearly has the benefit that the core precisely knows when to push an update. And funny enough, since the core does it actively, it can not be "disturbed" by update requests, which could come in any time, while a "push" can be done at a point where it's convenient for the core (or a push can even be delegated to a background thread). Now, as we both pointed out, with a pure "core push" model, there might be the danger that the receiver is no longer there and thus the sender crashes. However, this could be mitigated by using a combination of "push" and "pull". How about that? (1) core knows there is a change that must be published. Thus it 1a) gets a new identifier from the data block system (which we'll have to design and build from scratch for this purpose) 1b) places the data into the buffer associated with that ID 1c) sends a "there is a data update" message via the /existing/ ringbuffer-command system (that would be InterChange::toGui ) At this point, Core can just walk away. If no one picks up that message, then it will be overwritten in the ringbuffer eventually. And the claimed data block will also be re-used after some time. Thus: no problem here. (2) The GUI does already a duty-cycle in »mainThread«, which wait states of 33333µs. Currently this is used for the update and response messages. 2a) when GuiUpdates::decode_updates() detects the new message type it... 2b) retrieves the data block from the data block system and 2c) directly dispatches it to the intended GUI component (3) this GUI component holds what we've discussed already: a mirror data store. This data mirror now would be the receiver and would be *directly invoked* by »mainThread« to push the retrieved data block. From this point on, a draw() routine would use the new data. So far this seems to be straight forward and rather robust. There is one remaining issue: possible interference with other GUI processing. Maybe you know a little bit more about FLTK here? Where does FLTK process its own events? i.e. when the user clicks on a button or a window is uncovered and must be redrawn? Most UI toolkits I am aware of today use the model of "only one GUI thread", because this turned out to be the most robust way to build a message driven system. Thus typically there is somewhere a event-loop or a event-pulling in some GUI thread. Do you know how this is handled in Yoshimi? Is this GUI event processing hidden in the FL::wait() call which I see in mainThrad() Just poking into FL.h, I see a FL::run(), but this seems not to be invoked anywhere... -- Hermann _______________________________________________ Yoshimi-devel mailing list Yoshimi-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/yoshimi-devel