`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
Tyler Goodlet 2022-04-22 13:59:20 -04:00
parent 2af4050e5e
commit c94c53286b
1 changed files with 69 additions and 45 deletions

View File

@ -138,6 +138,7 @@ class FastAppendCurve(pg.GraphicsObject):
# brutaaalll, see comments within..
self._y = self.yData = y
self._x = self.xData = x
self._vr: Optional[tuple] = None
self._name = name
self.path: Optional[QtGui.QPainterPath] = None
@ -287,6 +288,17 @@ class FastAppendCurve(pg.GraphicsObject):
istart, istop = self._xrange
else:
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}")
# XXX: lol brutal, the internals of `CurvePoint` (inherited by
@ -295,37 +307,36 @@ class FastAppendCurve(pg.GraphicsObject):
# self.yData = y
# self._x, self._y = x, y
if view_range:
profiler(f'view range slice {view_range}')
# downsampling incremental state checking
uppx = self.x_uppx()
px_width = self.px_width()
uppx_diff = (uppx - self._last_uppx)
new_sample_rate = False
should_ds = False
showing_src_data = self._in_ds
should_redraw = False
# if a view range is passed, plan to draw the
# 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}')
# by default we only pull data up to the last (current) index
x_out, y_out = x_iv[:-1], y_iv[:-1]
profiler(f'view range slice {view_range}')
# step mode: draw flat top discrete "step"
# over the index space for each datum.
if self._step_mode:
# TODO: numba this bish
x_out, y_out = step_path_arrays_from_1d(
x_out,
y_out
)
profiler('generated step arrays')
if (
view_range != self._vr
and append_length > 1
):
should_redraw = True
should_redraw = True
profiler('sliced in-view array history')
self._vr = view_range
# x_last = x_iv[-1]
# y_last = y_iv[-1]
@ -335,7 +346,15 @@ class FastAppendCurve(pg.GraphicsObject):
# flip_cache = True
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
if (
@ -350,6 +369,8 @@ class FastAppendCurve(pg.GraphicsObject):
f'{self._name} sampler change: {self._last_uppx} -> {uppx}'
)
self._last_uppx = uppx
new_sample_rate = True
showing_src_data = False
should_ds = True
elif (
@ -360,49 +381,47 @@ class FastAppendCurve(pg.GraphicsObject):
# source data so we clear our path data in prep
# to generate a new one from original source data.
should_redraw = True
new_sample_rate = True
should_ds = False
# 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)
showing_src_data = True
# no_path_yet = self.path is None
if (
self.path is None
or should_redraw
or should_ds
or new_sample_rate
or prepend_length > 0
):
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 (
# 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]
# step mode: draw flat top discrete "step"
# over the index space for each datum.
if self._step_mode:
x_out, y_out = step_path_arrays_from_1d(
x_out,
y_out,
)
# TODO: numba this bish
profiler('generated step arrays')
# step mode: draw flat top discrete "step"
# over the index space for each datum.
if self._step_mode:
x_out, y_out = step_path_arrays_from_1d(
x_out,
y_out,
)
# TODO: numba this bish
profiler('generated step arrays')
if should_redraw:
profiler('path reversion to non-ds')
if self.path:
# print(f'CLEARING PATH {self._name}')
self.path.clear()
if self.fast_path:
self.fast_path.clear()
if should_redraw and not should_ds:
if self._in_ds:
log.info(f'DEDOWN -> {self._name}')
profiler('cleared paths due to `should_redraw` set')
if new_sample_rate and showing_src_data:
# if self._in_ds:
log.info(f'DEDOWN -> {self._name}')
self._in_ds = False
@ -423,7 +442,12 @@ class FastAppendCurve(pg.GraphicsObject):
finiteCheck=False,
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}')
# reserve mem allocs see:
@ -455,7 +479,7 @@ class FastAppendCurve(pg.GraphicsObject):
elif (
append_length > 0
and not view_range
# and not view_range
):
new_x = x[-append_length - 2:-1]
new_y = y[-append_length - 2:-1]
@ -696,7 +720,7 @@ class FastAppendCurve(pg.GraphicsObject):
if path:
p.drawPath(path)
profiler('.drawPath(path)')
profiler(f'.drawPath(path): {path.capacity()}')
fp = self.fast_path
if fp: