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

Reply via email to