`FastAppendCurve`: Only render in-view data if possible
More or less this improves update latency like mad. Only draw data in view and avoid full path regen as much as possible within a given (down)sampling setting. We now support append path updates with in-view data and the *SPECIAL CAVEAT* is that we avoid redrawing the whole curve **only when** we calc an `append_length <= 1` **even if the view range changed**. XXX: this should change in the future probably such that the caller graphics update code can pass a flag which says whether or not to do a full redraw based on it knowing where it's an interaction based view-range change or a flow update change which doesn't require a full path re-render.incremental_update_paths
parent
2af4050e5e
commit
c94c53286b
|
@ -138,6 +138,7 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
# brutaaalll, see comments within..
|
# brutaaalll, see comments within..
|
||||||
self._y = self.yData = y
|
self._y = self.yData = y
|
||||||
self._x = self.xData = x
|
self._x = self.xData = x
|
||||||
|
self._vr: Optional[tuple] = None
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self.path: Optional[QtGui.QPainterPath] = None
|
self.path: Optional[QtGui.QPainterPath] = None
|
||||||
|
@ -287,6 +288,17 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
istart, istop = self._xrange
|
istart, istop = self._xrange
|
||||||
else:
|
else:
|
||||||
self._xrange = istart, istop = x[0], x[-1]
|
self._xrange = istart, istop = x[0], x[-1]
|
||||||
|
|
||||||
|
# compute the length diffs between the first/last index entry in
|
||||||
|
# the input data and the last indexes we have on record from the
|
||||||
|
# last time we updated the curve index.
|
||||||
|
prepend_length = int(istart - x[0])
|
||||||
|
append_length = int(x[-1] - istop)
|
||||||
|
|
||||||
|
# this is the diff-mode, "data"-rendered index
|
||||||
|
# tracking var..
|
||||||
|
self._xrange = x[0], x[-1]
|
||||||
|
|
||||||
# print(f"xrange: {self._xrange}")
|
# print(f"xrange: {self._xrange}")
|
||||||
|
|
||||||
# XXX: lol brutal, the internals of `CurvePoint` (inherited by
|
# XXX: lol brutal, the internals of `CurvePoint` (inherited by
|
||||||
|
@ -295,37 +307,36 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
# self.yData = y
|
# self.yData = y
|
||||||
# self._x, self._y = x, y
|
# self._x, self._y = x, y
|
||||||
|
|
||||||
if view_range:
|
|
||||||
profiler(f'view range slice {view_range}')
|
|
||||||
|
|
||||||
# downsampling incremental state checking
|
# downsampling incremental state checking
|
||||||
uppx = self.x_uppx()
|
uppx = self.x_uppx()
|
||||||
px_width = self.px_width()
|
px_width = self.px_width()
|
||||||
uppx_diff = (uppx - self._last_uppx)
|
uppx_diff = (uppx - self._last_uppx)
|
||||||
|
|
||||||
|
new_sample_rate = False
|
||||||
should_ds = False
|
should_ds = False
|
||||||
|
showing_src_data = self._in_ds
|
||||||
should_redraw = False
|
should_redraw = False
|
||||||
|
|
||||||
# if a view range is passed, plan to draw the
|
# if a view range is passed, plan to draw the
|
||||||
# source ouput that's "in view" of the chart.
|
# source ouput that's "in view" of the chart.
|
||||||
if view_range and not self._in_ds:
|
if (
|
||||||
|
view_range
|
||||||
|
# and not self._in_ds
|
||||||
|
# and not prepend_length > 0
|
||||||
|
):
|
||||||
# print(f'{self._name} vr: {view_range}')
|
# print(f'{self._name} vr: {view_range}')
|
||||||
|
|
||||||
# by default we only pull data up to the last (current) index
|
# by default we only pull data up to the last (current) index
|
||||||
x_out, y_out = x_iv[:-1], y_iv[:-1]
|
x_out, y_out = x_iv[:-1], y_iv[:-1]
|
||||||
|
profiler(f'view range slice {view_range}')
|
||||||
|
|
||||||
# step mode: draw flat top discrete "step"
|
if (
|
||||||
# over the index space for each datum.
|
view_range != self._vr
|
||||||
if self._step_mode:
|
and append_length > 1
|
||||||
# TODO: numba this bish
|
):
|
||||||
x_out, y_out = step_path_arrays_from_1d(
|
|
||||||
x_out,
|
|
||||||
y_out
|
|
||||||
)
|
|
||||||
profiler('generated step arrays')
|
|
||||||
|
|
||||||
should_redraw = True
|
should_redraw = True
|
||||||
profiler('sliced in-view array history')
|
|
||||||
|
self._vr = view_range
|
||||||
|
|
||||||
# x_last = x_iv[-1]
|
# x_last = x_iv[-1]
|
||||||
# y_last = y_iv[-1]
|
# y_last = y_iv[-1]
|
||||||
|
@ -335,7 +346,15 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
# flip_cache = True
|
# flip_cache = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self._xrange = x[0], x[-1]
|
# if (
|
||||||
|
# not view_range
|
||||||
|
# or self._in_ds
|
||||||
|
# ):
|
||||||
|
# by default we only pull data up to the last (current) index
|
||||||
|
x_out, y_out = x[:-1], y[:-1]
|
||||||
|
|
||||||
|
if prepend_length > 0:
|
||||||
|
should_redraw = True
|
||||||
|
|
||||||
# check for downsampling conditions
|
# check for downsampling conditions
|
||||||
if (
|
if (
|
||||||
|
@ -350,6 +369,8 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
f'{self._name} sampler change: {self._last_uppx} -> {uppx}'
|
f'{self._name} sampler change: {self._last_uppx} -> {uppx}'
|
||||||
)
|
)
|
||||||
self._last_uppx = uppx
|
self._last_uppx = uppx
|
||||||
|
new_sample_rate = True
|
||||||
|
showing_src_data = False
|
||||||
should_ds = True
|
should_ds = True
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
|
@ -360,27 +381,23 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
# source data so we clear our path data in prep
|
# source data so we clear our path data in prep
|
||||||
# to generate a new one from original source data.
|
# to generate a new one from original source data.
|
||||||
should_redraw = True
|
should_redraw = True
|
||||||
|
new_sample_rate = True
|
||||||
should_ds = False
|
should_ds = False
|
||||||
|
showing_src_data = True
|
||||||
# compute the length diffs between the first/last index entry in
|
|
||||||
# the input data and the last indexes we have on record from the
|
|
||||||
# last time we updated the curve index.
|
|
||||||
prepend_length = int(istart - x[0])
|
|
||||||
append_length = int(x[-1] - istop)
|
|
||||||
|
|
||||||
# no_path_yet = self.path is None
|
# no_path_yet = self.path is None
|
||||||
if (
|
if (
|
||||||
self.path is None
|
self.path is None
|
||||||
or should_redraw
|
or should_redraw
|
||||||
or should_ds
|
or new_sample_rate
|
||||||
or prepend_length > 0
|
or prepend_length > 0
|
||||||
):
|
):
|
||||||
if (
|
# if (
|
||||||
not view_range
|
# not view_range
|
||||||
or self._in_ds
|
# or self._in_ds
|
||||||
):
|
# ):
|
||||||
# by default we only pull data up to the last (current) index
|
# # by default we only pull data up to the last (current) index
|
||||||
x_out, y_out = x[:-1], y[:-1]
|
# x_out, y_out = x[:-1], y[:-1]
|
||||||
|
|
||||||
# step mode: draw flat top discrete "step"
|
# step mode: draw flat top discrete "step"
|
||||||
# over the index space for each datum.
|
# over the index space for each datum.
|
||||||
|
@ -393,15 +410,17 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
profiler('generated step arrays')
|
profiler('generated step arrays')
|
||||||
|
|
||||||
if should_redraw:
|
if should_redraw:
|
||||||
profiler('path reversion to non-ds')
|
|
||||||
if self.path:
|
if self.path:
|
||||||
|
# print(f'CLEARING PATH {self._name}')
|
||||||
self.path.clear()
|
self.path.clear()
|
||||||
|
|
||||||
if self.fast_path:
|
if self.fast_path:
|
||||||
self.fast_path.clear()
|
self.fast_path.clear()
|
||||||
|
|
||||||
if should_redraw and not should_ds:
|
profiler('cleared paths due to `should_redraw` set')
|
||||||
if self._in_ds:
|
|
||||||
|
if new_sample_rate and showing_src_data:
|
||||||
|
# if self._in_ds:
|
||||||
log.info(f'DEDOWN -> {self._name}')
|
log.info(f'DEDOWN -> {self._name}')
|
||||||
|
|
||||||
self._in_ds = False
|
self._in_ds = False
|
||||||
|
@ -423,7 +442,12 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
finiteCheck=False,
|
finiteCheck=False,
|
||||||
path=self.path,
|
path=self.path,
|
||||||
)
|
)
|
||||||
profiler('generated fresh path')
|
profiler(
|
||||||
|
'generated fresh path\n'
|
||||||
|
f'should_redraw: {should_redraw}\n'
|
||||||
|
f'should_ds: {should_ds}\n'
|
||||||
|
f'new_sample_rate: {new_sample_rate}\n'
|
||||||
|
)
|
||||||
# profiler(f'DRAW PATH IN VIEW -> {self._name}')
|
# profiler(f'DRAW PATH IN VIEW -> {self._name}')
|
||||||
|
|
||||||
# reserve mem allocs see:
|
# reserve mem allocs see:
|
||||||
|
@ -455,7 +479,7 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
append_length > 0
|
append_length > 0
|
||||||
and not view_range
|
# and not view_range
|
||||||
):
|
):
|
||||||
new_x = x[-append_length - 2:-1]
|
new_x = x[-append_length - 2:-1]
|
||||||
new_y = y[-append_length - 2:-1]
|
new_y = y[-append_length - 2:-1]
|
||||||
|
@ -696,7 +720,7 @@ class FastAppendCurve(pg.GraphicsObject):
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
p.drawPath(path)
|
p.drawPath(path)
|
||||||
profiler('.drawPath(path)')
|
profiler(f'.drawPath(path): {path.capacity()}')
|
||||||
|
|
||||||
fp = self.fast_path
|
fp = self.fast_path
|
||||||
if fp:
|
if fp:
|
||||||
|
|
Loading…
Reference in New Issue