back

OxyScope (swap chain) buffering

OxyScope has 3 modes for collecting SimHub property values:
grow range
starts with a capture, based on samples per shot,
then appends samples different from those previously captured,
up to the total buffer limit.
This single buffer is shared for plotting;
capturing may be paused by changing Auto to Hold.
then resumed or restarted.
one shot
Captures of length samples per shot can be plotted, while capture continues.  This wants at least ping-pong buffering,
to avoid data changes during plot by subsequent capture.
However, a chain of 3 buffers would enable
plotting another buffer immediately after a plot is dismissed.
Currently, with only two buffers,
the unplotted buffer is likely incomplete when refresh is wanted.
more range
OxyScope searches for capture of length samples per shot
with wider range of values for some selected properties.
This wants a chain of at least 3 buffers, allowing
OxyScope to continue searching for a larger range
than one currently held, but not yet displayed,
while reprocessing and replotting a third buffer.

I feel the need


While 3 swap chain buffer benefits were recognized early in OxyScope development,
it did not become so important until VS and rescale options,
which want to manipulate and replot currently buffered data.
Before, capture could resume modifying second swap buffer as soon as its data had been plotted.

Sadly, C# examples for swap-chain buffering are all about display buffering and tied to specifics of some display rendering architecture

handshaking

Plotting and user interactions are handled on a thread
separate from that used for processing SimHub properties.
grow range
Because a single buffer, DataUpdate() simply does nothing if !AutoPlot
one shot
With multiple buffers, DataUpdate() continues collecting...
Replot() may become !busy at any time.
- if a newer (one shot) or bigger more range buffer is available,
then call Replot() before the current work buffer completes.
when current work buffer completes,
mark buffer available and switch to other buffer.
more range
similar to one shot, except available wants buffer with larger value range.

available handling

New variables:  bool available; byte VM.plot, other // buffer indices
Invoke `Replot()` asynchronusly:
if (available && !VM.busy)
{
    available = false;
    VM.busy = true;
    VM.plot = other;						// buffer index for Replot()
    other = (byte)(3 - (VM.plot + work));
    View.Dispatcher.Invoke(() => View.Replot(VM.Slength));
}
one shot or more range buffer complete
if ((++Sample - VM.start[work]) >= VM.Slength) // filled?
{
    // swap chain buffer:  buffer setup in Control.xaml.cs Control() and Model.Slength
    // https://blekenbleu.github.io/SimHub/swapchain.htm
    double work_range = Drange(work, VM.property);

    VM.Current = $"{VM.min[work][clf]:#0.000} <= Y <= {VM.max[work][clf]:#0.000};  "
               + ((1 == VM.Refresh) ? $"{VM.min[work][3]:#0.000} <= X <= {VM.max[work][3]:#0.000}"
                                    : $"{VM.PropName[VM.property]} range {work_range}");

    // Refresh: 0 = greater range, 1 = snapshot, 2 = grow range (not handled here)
    // property: 3 == no curve fitting; 0-2 correspond to Y0-Y2
    if (1 == VM.Refresh || Drange(other, VM.property) < work_range)
    {
        other = work;
        work = (byte)(3 - (other + VM.plot));
    }
    available = 1 == VM.Refresh || Drange(VM.plot, VM.property) < Drange(other, VM.property);
    Sample = VM.start[work];
}
maintained by blekenbleu