joewalnes / smoothie

Smoothie Charts: smooooooth JavaScript charts for realtime streaming data
http://smoothiecharts.org
Other
2.24k stars 229 forks source link

Draw lag #154

Closed nounderstanding closed 1 month ago

nounderstanding commented 1 month ago

Using your library to draw EEG with 64 channels, each channel updates data for 2 points every 100 milliseconds. At this time, there may be a lag in the drawing. Can this problem be solved? Or could you describe the reason for this problem?

1
WofWca commented 1 month ago

"Might be lag"? Does it not happen every frame? Do you have the source code? Or a video recording? Or a performance profiling file?

The data is kinda huge NGL, so it might really be the case that rendering cannot be faster. Smoothie re-renders every frame from scratch.

nounderstanding commented 1 month ago

But when the point on a line changes from -10000 to 10000, it will be too laggy. It is obvious on Windows computers that the data value changes greatly, and it will be stuck when there is a lot of data

nounderstanding commented 1 month ago
<style lang="scss">
.smoothie-chart-tooltip {
  background: #444;
  padding: 1em;
  margin-top: 20px;
  font-family: "consolas";
  color: white;
  font-size: 14px;
  pointer-events: none;
}
</style>
<style lang="scss" scoped>
.container {
  overflow: hidden;
  background-color: #353535;
  .row {
    width: 100%;
    display: flex;
    .col-1 {
      width: 100px;
    }
    .col-11 {
      flex: 1;
    }
  }
  .align-div {
    text-align: center;
    color: #fff;
    font-weight: bold;
    height: 60px;
    position: relative;
  }

  .align-to-graph {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
  }

  canvas {
    border: solid 1px #333;
  }
}
</style>
<template>
  <div class="container">
    <div class="row">
      <div class="col-1">
        <div
          v-for="channel in channels"
          :style="{ height: height + 'px' }"
          class="align-div"
          :key="channel"
        >
          <span class="align-to-graph">{{ channel }}</span>
        </div>
      </div>
      <div class="col-11">
        <canvas
          id="time-series"
          style="width: 100%"
          :style="{ height: height * channels.length + 'px' }"
        ></canvas>
      </div>
    </div>
  </div>
</template>

<script>
import { SmoothieChart, TimeSeries } from "../../utils/smoothie";
const COLORS = [
  "#3cb44b",
  "#03cea4",
  "#f07537",
  "#ff6384",
  "#9c51ba",
  "#d4b2d8",
  "#222",
  "#b7c448",
  "#4272bf",
  "#36a2eb",
  "#ffce56",
  "#f7464a",
  "#ffffff",
  "#add8e6",
  "#e5a2c2",
  "#4bc0c0",
  "#3cb44b",
  "#03cea4",
  "#f07537",
  "#ff6384",
  "#9c51ba",
  "#d4b2d8",
  "#222",
  "#b7c448",
  "#4272bf",
  "#36a2eb",
  "#ffce56",
  "#f7464a",
  "#ffffff",
  "#add8e6",
  "#e5a2c2",
  "#4bc0c0",
  "#3cb44b",
  "#03cea4",
  "#f07537",
  "#ff6384",
  "#9c51ba",
  "#d4b2d8",
  "#222",
  "#b7c448",
  "#4272bf",
  "#36a2eb",
  "#ffce56",
  "#f7464a",
  "#ffffff",
  "#add8e6",
  "#e5a2c2",
  "#4bc0c0",
  "#3cb44b",
  "#03cea4",
  "#f07537",
  "#ff6384",
  "#9c51ba",
  "#d4b2d8",
  "#222",
  "#b7c448",
  "#4272bf",
  "#36a2eb",
  "#ffce56",
  "#f7464a",
  "#ffffff",
  "#add8e6",
  "#e5a2c2",
  "#4bc0c0"
];

const getColor = colorNumber => {
  return COLORS[colorNumber % COLORS.length];
};
function getRandomNumber(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
const maxValidValue = 2;

const offsetSignal = (
  amplitude,
  channelNumber,
  channelAmount = 6,
  unit = "V", // V uV
  scale = 1
) => {
  if (unit == "uV") amplitude = amplitude / 1000000;
  let scaledAmplitude = amplitude * scale;
  let offset = maxValidValue * (channelAmount - channelNumber - 1);
  return parseFloat(scaledAmplitude) + offset;
};

const channels = "Cz,CP1,C4,Pz,O1,Fp1,CP6,O2,CP2,P8,C3,P7,Fp2,F8,CP5,F7,HEOL,FC5,HEOR,P4,T7,F3,T8,FC2,PO4,FC6,P3,PO3,Oz,F4,FC1,Fz,FC4,Fpz,P6,FCz,C6,POz,F6,PO6,PO8,C2,TP8,F2,FT8,AF4,AF8,CP4,AF7,CP3,FT7,AF3,TP7,F1,PO7,C1,F5,PO5,C5,ECG,P5,VEOL,FC3,VEOU".split(
  ","
);

export default {
  data() {
    return {
      height: 50,
      channels,
      charts: [],
      series: [],
      init: false,
      unit: "V",
      scale: 1
    };
  },
  created() {
    setInterval(() => {
      if (this.init) {
        let start = performance.now();
        for (let j = 0; j < this.channels.length; j++) {
          for (let i = 0; i < 80; i++) {
            this.updateChannelData(
              j,
              Date.now(),
              getRandomNumber(-100000, 180000)
            );
          }
        }
        let end = performance.now();
        console.log(end - start);
      }
    }, 77);
  },
  mounted() {
    const canvas = document.getElementById("time-series");
    this.charts = new SmoothieChart({
      millisPerPixel: 10,
      responsive: true,
      tooltip: true,
      tooltipLine: {
        strokeStyle: "#FF0000"
      },
      grid: {
        fillStyle: "#555",
        strokeStyle: "rgba(0,0,0,0.1)",
        sharpLines: false,
        verticalSections: this.channels.length,
        borderVisible: true
      },
      maxValue: this.channels.length * maxValidValue,
      minValue: 0,
      interpolation: "linear",
      labels: {
        disabled: true,
        fillStyle: "#fff",
        fontSize: 12
      }
    });
    this.channels.forEach((channel, idx) => {
      this.series.push(new TimeSeries());
      this.charts.addTimeSeries(this.series[idx], {
        strokeStyle: getColor(idx)
      });
    });
    // render chart
    this.charts.streamTo(canvas, 0);
    // this.charts.options.limitFPS = 60;
    this.init = true;
  },
  methods: {
    updateChannelData(idx, timestamp, data) {
      let serie = this.series[idx];
      serie.append(
        timestamp,
        offsetSignal(data, idx, this.channels.length, this.unit, this.scale)
      );
    }
  }
};
</script>

The CPU usage rate is 54% The GPU usage rate is 35% https://github.com/joewalnes/smoothie/assets/22467963/4dd51eb9-56e0-4ebb-b821-ee5d85929983

WofWca commented 1 month ago

Idk, with the following code there is no significant lag for me. I go to http://smoothiecharts.org/ and paste the code in the console. Please see if this is the same for you.

Code ```javascript const COLORS = [ "#3cb44b", "#03cea4", "#f07537", "#ff6384", "#9c51ba", "#d4b2d8", "#222", "#b7c448", "#4272bf", "#36a2eb", "#ffce56", "#f7464a", "#ffffff", "#add8e6", "#e5a2c2", "#4bc0c0", "#3cb44b", "#03cea4", "#f07537", "#ff6384", "#9c51ba", "#d4b2d8", "#222", "#b7c448", "#4272bf", "#36a2eb", "#ffce56", "#f7464a", "#ffffff", "#add8e6", "#e5a2c2", "#4bc0c0", "#3cb44b", "#03cea4", "#f07537", "#ff6384", "#9c51ba", "#d4b2d8", "#222", "#b7c448", "#4272bf", "#36a2eb", "#ffce56", "#f7464a", "#ffffff", "#add8e6", "#e5a2c2", "#4bc0c0", "#3cb44b", "#03cea4", "#f07537", "#ff6384", "#9c51ba", "#d4b2d8", "#222", "#b7c448", "#4272bf", "#36a2eb", "#ffce56", "#f7464a", "#ffffff", "#add8e6", "#e5a2c2", "#4bc0c0" ]; const getColor = colorNumber => { return COLORS[colorNumber % COLORS.length]; }; function getRandomNumber(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } const maxValidValue = 2; const offsetSignal = ( amplitude, channelNumber, channelAmount = 6, unit = "V", // V uV scale = 1 ) => { if (unit == "uV") amplitude = amplitude / 1000000; let scaledAmplitude = amplitude * scale; let offset = maxValidValue * (channelAmount - channelNumber - 1); return parseFloat(scaledAmplitude) + offset; }; const channels = "Cz,CP1,C4,Pz,O1,Fp1,CP6,O2,CP2,P8,C3,P7,Fp2,F8,CP5,F7,HEOL,FC5,HEOR,P4,T7,F3,T8,FC2,PO4,FC6,P3,PO3,Oz,F4,FC1,Fz,FC4,Fpz,P6,FCz,C6,POz,F6,PO6,PO8,C2,TP8,F2,FT8,AF4,AF8,CP4,AF7,CP3,FT7,AF3,TP7,F1,PO7,C1,F5,PO5,C5,ECG,P5,VEOL,FC3,VEOU".split( "," ); const component = { data() { return { height: 50, channels, charts: [], series: [], init: false, unit: "V", scale: 1 }; }, created() { setInterval(() => { if (this.init) { let start = performance.now(); for (let j = 0; j < this.channels.length; j++) { for (let i = 0; i < 80; i++) { this.updateChannelData( j, Date.now(), getRandomNumber(-100000, 180000) ); } } let end = performance.now(); console.log(end - start); } }, 77); }, mounted() { const canvas = document.getElementById("time-series"); this.charts = new SmoothieChart({ millisPerPixel: 10, responsive: true, tooltip: true, tooltipLine: { strokeStyle: "#FF0000" }, grid: { fillStyle: "#555", strokeStyle: "rgba(0,0,0,0.1)", sharpLines: false, verticalSections: this.channels.length, borderVisible: true }, maxValue: this.channels.length * maxValidValue, minValue: 0, interpolation: "linear", labels: { disabled: true, fillStyle: "#fff", fontSize: 12 } }); this.channels.forEach((channel, idx) => { this.series.push(new TimeSeries()); this.charts.addTimeSeries(this.series[idx], { strokeStyle: getColor(idx) }); }); // render chart this.charts.streamTo(canvas, 0); // this.charts.options.limitFPS = 60; this.init = true; }, updateChannelData(idx, timestamp, data) { let serie = this.series[idx]; serie.append( timestamp, offsetSignal(data, idx, this.channels.length, this.unit, this.scale) ); } } const canvas = document.createElement('canvas') canvas.id = 'time-series'; canvas.style.width = "1500px" canvas.style.height = "1000px" document.body.prepend(canvas) Object.assign(component, component.data()); component.created(); component.mounted(); ```

when the point on a line changes from -10000 to 10000, it will be too laggy

Perhaps try the scaleSmoothing: 1 option.

nounderstanding commented 1 month ago

When I set scaleSmoothing:1, it becomes super sluggish, and when I set interpolation:'bezier', it also becomes super sluggish.

browser : Microsoft Edge

I am using the device: Device Name: LAPTOP-LNAR8SE9(lenovo) processor: Intel (R) Core (TM) i7-8565U CPU @ 1.80GHz 1.99 GHz Machine with RAM: 16.0 GB (15.8 GB available) system: Windows 11 Professional Edition

nounderstanding commented 1 month ago

I tested the above code on different models of computers, and some of them experienced severe lag; Some computers don't lag May I ask, what is the possible cause of interface lag?

WofWca commented 1 month ago

I'm not sure. I think it would be much easier to figure it out if you ran the profiler.

nounderstanding commented 1 month ago

Thank you, I'll give it a try

nounderstanding commented 1 month ago

Sorry, there is no problem with your code

WofWca commented 1 month ago

Well, it's still performing poorly isn't it? But OK.