`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.incr_update_backup
							parent
							
								
									3c58847595
								
							
						
					
					
						commit
						cde23361a4
					
				| 
						 | 
					@ -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(
 | 
					                should_redraw = True
 | 
				
			||||||
                    x_out,
 | 
					 | 
				
			||||||
                    y_out
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                profiler('generated step arrays')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            should_redraw = True
 | 
					            self._vr = view_range
 | 
				
			||||||
            profiler('sliced in-view array history')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # 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,49 +381,47 @@ 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.
 | 
				
			||||||
                if self._step_mode:
 | 
					            if self._step_mode:
 | 
				
			||||||
                    x_out, y_out = step_path_arrays_from_1d(
 | 
					                x_out, y_out = step_path_arrays_from_1d(
 | 
				
			||||||
                        x_out,
 | 
					                    x_out,
 | 
				
			||||||
                        y_out,
 | 
					                    y_out,
 | 
				
			||||||
                    )
 | 
					                )
 | 
				
			||||||
                    # TODO: numba this bish
 | 
					                # TODO: numba this bish
 | 
				
			||||||
                    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:
 | 
					
 | 
				
			||||||
                    log.info(f'DEDOWN -> {self._name}')
 | 
					            if new_sample_rate and showing_src_data:
 | 
				
			||||||
 | 
					                # if self._in_ds:
 | 
				
			||||||
 | 
					                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