-
Notifications
You must be signed in to change notification settings - Fork 93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[5pt,5pt] overhaul of (DataSet) event system #530
Comments
Matching the ideas I've mentioned before, I created an event framework based on bit masks where listeners can subscribe to dirty bits that they are interested in. Similar to JavaFX properties, each element can subscribe to others via change-listeners (called if specified bits change from 0 to 1) or invalidation-listeners (called on every event). Example for an axis that needs to trigger a layout on a property change // create an axis state object that knows only about axis events
var state = BitState.initDirty(this, ChartBits.AxisMask);
// changes to the axis padding need to recompute the layout and redraw the canvas
// (the set method has the same signature as a JavaFX listener, but without introducing a dependency)
axisPadding.addListener(state.onPropChange(ChartBits.AxisLayout, ChartBits.AxisCanvas)::set);
// trigger JavaFX layouts
state.addChangeListener(ChartBits.AxisLayout, (src, bits) -> requestLayout()); Example for a chart that subscribes and unsubscribes to an axis: // create a chart state object that knows about all events
var state = BitState.initDirty(this, ChartBits.AxisMask);
// merge changes coming from a relevant axis
axis.getBitState().addChangeListener(this.state);
// trigger a redraw if any axis needs to be drawn
state.addChangeListener(ChartBits.AxisCanvas, (src, bits) -> drawAxesInNextCycle());
// remove an axis that is no longer part of this chart
axis.getBitState().removeChangeListener(this.state); Example for skipping the draw step if none of the axes has changed redrawAxes() {
if (state.isClean(ChartBits.AxisCanvas) {
return; // all content is still good
}
for (var axis : getAxes) {
axis.redraw();
}
} The state bits get cleared after all elements are drawn. Updates use bitwise operations and batch updates automatically, so the process is very efficient. For example 10 data sets changing 100 values will get merged into 10 dataset events that are then merged into a single chart event. The merging/batching also simplifies scenarios where datasets are updated from non-JavaFX threads, e.g., with a simple dataSet.setDirty(ChartBits.Data, CharBits.DataRange);
dataSet.getBitState().addChangeListener(this.state, (src, bits) -> runOnFx(() -> this.state.setDirty(bits)); Depending on the needs we could also have variants with thread-local state and/or smarter locking. I also added debugging utilities to check what triggered an events, e.g., // Print every time something needs the canvas content to be updated
state.addInvalidateListener(ChartBits.AxisCanvas, ChartBits.printerWithStackTrace()); e.g. a redraw call coming from At the low-level it's based in enum CustomEvents implements IntSupplier {
SomeBit;
int getAsInt() {
return bit;
}
final int bit = 1 << (ChartEvents.values().length + ordinal());
}
state.setDirty(CustomEvents.SomeBit); The |
The event system has been tried on the entire chart and works as expected |
Between #527 and #579 the following things still need to be looked at. Minor issues pre merge
Minor checks
Future work
Require further discussionConcurrency in dependent DataSetsSome datasets, i.e., Concurrent computations should not hold on to the lock to avoid halting the rendering, so they should do a full copy of the relevant data, e.g.,
I currently envision 3 different modes. For concurrent processing it may be nice to show a progress indicator on the chart or on the legend symbol.
Another question is when to trigger the background thread. I can currently think of a few options
|
The new event system aggregates events and deterministically draws all state at once. Besides consistency benefits, this results in significant performance gains as it does not redraw the same content multiple times. Below are some manually gathered counters for how often individual methods run for certain actions.
|
The old event system classes are still in there for backwards compatibility. They can be removed once the measurements and dependent datasets are migrated fully (out of scope for this PR). |
Goals:
more efficient, thread-safe event processing
event system should work without depending on the javafx framework
allow "ParameterMeasurement" style dataset postprocessing defined by the user at runtime
Items to be investigated/implemented
The text was updated successfully, but these errors were encountered: