diff --git a/assets/gl-bench.js b/assets/gl-bench.js
index 0fb6bc4f..e7cb26bd 100644
--- a/assets/gl-bench.js
+++ b/assets/gl-bench.js
@@ -1,48 +1,40 @@
-// downloaded from https://github.com/munrocket/gl-bench
-// this file is https://github.com/munrocket/gl-bench/blob/master/dist/gl-bench.module.js
-
-/*
-var UISVG = "
\n
\n
\n
\n
";
-
-var UICSS = "#gl-bench {\n position:absolute;\n left:0;\n top:0;\n z-index:1000;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n\n#gl-bench div {\n position: relative;\n display: block;\n margin: 4px;\n padding: 0 7px 0 10px;\n background: #6c6;\n border-radius: 15px;\n cursor: pointer;\n opacity: 0.9;\n}\n\n#gl-bench svg {\n height: 60px;\n margin: 0 -1px;\n}\n\n#gl-bench text {\n font-size: 12px;\n font-family: Helvetica,Arial,sans-serif;\n font-weight: 700;\n dominant-baseline: middle;\n text-anchor: middle;\n}\n\n#gl-bench .gl-mem {\n font-size: 9px;\n}\n\n#gl-bench line {\n stroke-width: 5;\n stroke: #112211;\n stroke-linecap: round;\n}\n\n#gl-bench polyline {\n fill: none;\n stroke: #112211;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 3.5;\n}\n\n#gl-bench rect {\n fill: #448844;\n}\n\n#gl-bench .opacity {\n stroke: #448844;\n}\n";
-*/
-
-const UISVG = `
-
-
-
-
-
- `;
+// modified based on: https://github.com/munrocket/gl-bench
const UICSS = `
- #gl-bench { position: absolute; right:0; bottom:0; z-index:1000; -webkit-user-select: none; -moz-user-select: none; user-select: none; }
+ #gl-bench { position: absolute; right: 1rem; bottom: 1rem; z-index:1000; -webkit-user-select: none; -moz-user-select: none; user-select: none; }
#gl-bench div { position: relative; display: block; margin: 4px; padding: 0 7px 0 10px; background: darkslategray; border-radius: 0.2rem; cursor: pointer; opacity: 0.9; }
- #gl-bench svg { height: 60px; margin: 0 -1px; }
- #gl-bench text { font-size: 12px; font-family: Helvetica,Arial,sans-serif; font-weight: 700; dominant-baseline: middle; text-anchor: middle; }
- #gl-bench .gl-mem { font-size: 9px; fill: white; }
- #gl-bench .gl-fps { font-size: 10px; fill: white; }
+ #gl-bench svg { height: 60px; margin: 0 4px 0px 4px; }
+ #gl-bench text { font-size: 16px; font-family: 'Lato', 'Segoe UI'; dominant-baseline: middle; text-anchor: middle; }
+ #gl-bench .gl-mem { font-size: 12px; fill: white; }
+ #gl-bench .gl-fps { font-size: 13px; fill: white; }
#gl-bench line { stroke-width: 5; stroke: white; stroke-linecap: round; }
#gl-bench polyline { fill: none; stroke: white; stroke-linecap: round; stroke-linejoin: round; stroke-width: 3.5; }
#gl-bench rect { fill: black; }
#gl-bench .opacity { stroke: black; }
`;
-class GLBench {
+const UISVG = `
+
+
+
+
+
+ `;
+class GLBench {
/** GLBench constructor
* @param { WebGLRenderingContext | WebGL2RenderingContext } gl context
* @param { Object | undefined } settings additional settings
@@ -57,14 +49,12 @@ class GLBench {
this.names = [];
this.cpuAccums = [];
- this.gpuAccums = [];
+ this.gpuAccums = [];
this.activeAccums = [];
this.chart = new Array(this.chartLen);
- this.now = () => (performance && performance.now) ? performance.now() : Date.now();
+ this.now = () => ((performance && performance.now) ? performance.now() : Date.now());
this.updateUI = () => {
- [].forEach.call(this.nodes['gl-gpu-svg'], node => {
- node.style.display = this.trackGPU ? 'inline' : 'none';
- });
+ [].forEach.call(this.nodes['gl-gpu-svg'], (node) => node.style.display = this.trackGPU ? 'inline' : 'none');
};
Object.assign(this, settings);
@@ -74,8 +64,9 @@ class GLBench {
this.frameId = 0;
// 120hz device detection
- let rafId, n = 0, t0;
- let loop = (t) => {
+ let rafId; let n = 0; let
+ t0;
+ const loop = (t) => {
if (++n < 20) {
rafId = requestAnimationFrame(loop);
} else {
@@ -88,31 +79,27 @@ class GLBench {
// attach gpu profilers
if (gl) {
- const glFinish = async (t, activeAccums) =>
- Promise.resolve(setTimeout(() => {
- gl.getError();
- const dt = this.now() - t;
- activeAccums.forEach((active, i) => {
- if (active) this.gpuAccums[i] += dt;
- });
- }, 0));
+ const glFinish = async (t, activeAccums) => Promise.resolve(setTimeout(() => {
+ gl.getError();
+ const dt = this.now() - t;
+ activeAccums.forEach((active, i) => {
+ if (active) this.gpuAccums[i] += dt;
+ });
+ }, 0));
- const addProfiler = (fn, self, target) => function() {
+ const addProfiler = (fn, self, target) => function () {
const t = self.now();
+ // eslint-disable-next-line prefer-rest-params
fn.apply(target, arguments);
if (self.trackGPU) self.finished.push(glFinish(t, self.activeAccums.slice(0)));
};
- ['drawArrays', 'drawElements', 'drawArraysInstanced',
- 'drawBuffers', 'drawElementsInstanced', 'drawRangeElements']
- .forEach(fn => { if (gl[fn]) gl[fn] = addProfiler(gl[fn], this, gl); });
+ ['drawArrays', 'drawElements', 'drawArraysInstanced', 'drawBuffers', 'drawElementsInstanced', 'drawRangeElements'].forEach((fn) => { if (gl[fn]) gl[fn] = addProfiler(gl[fn], this, gl); });
- gl.getExtension = ((fn, self) => function() {
- let ext = fn.apply(gl, arguments);
- if (ext) {
- ['drawElementsInstancedANGLE', 'drawBuffersWEBGL']
- .forEach(fn => { if (ext[fn]) ext[fn] = addProfiler(ext[fn], self, ext); });
- }
+ gl.getExtension = ((fn, self) => function () {
+ // eslint-disable-next-line prefer-rest-params
+ const ext = fn.apply(gl, arguments);
+ if (ext) ['drawElementsInstancedANGLE', 'drawBuffersWEBGL'].forEach((fn2) => { if (ext[fn2]) ext[fn2] = addProfiler(ext[fn2], self, ext); });
return ext;
})(gl.getExtension, this);
}
@@ -120,7 +107,7 @@ class GLBench {
// init ui and ui loggers
if (!this.withoutUI) {
if (!this.dom) this.dom = document.body;
- let elm = document.createElement('div');
+ const elm = document.createElement('div');
elm.id = 'gl-bench';
this.dom.appendChild(elm);
this.dom.insertAdjacentHTML('afterbegin', '');
@@ -132,43 +119,41 @@ class GLBench {
this.paramLogger = ((logger, dom, names) => {
const classes = ['gl-cpu', 'gl-gpu', 'gl-mem', 'gl-fps', 'gl-gpu-svg', 'gl-chart'];
- const nodes = Object.assign({}, classes);
- classes.forEach(c => nodes[c] = dom.getElementsByClassName(c));
+ const nodes = { ...classes };
+ classes.forEach((c) => nodes[c] = dom.getElementsByClassName(c));
this.nodes = nodes;
return (i, cpu, gpu, mem, fps, totalTime, frameId) => {
nodes['gl-cpu'][i].style.strokeDasharray = (cpu * 0.27).toFixed(0) + ' 100';
nodes['gl-gpu'][i].style.strokeDasharray = (gpu * 0.27).toFixed(0) + ' 100';
+ // eslint-disable-next-line no-nested-ternary
nodes['gl-mem'][i].innerHTML = names[i] ? names[i] : (mem ? 'mem: ' + mem.toFixed(0) + 'mb' : '');
nodes['gl-fps'][i].innerHTML = fps.toFixed(0) + ' FPS';
logger(names[i], cpu, gpu, mem, fps, totalTime, frameId);
- }
+ };
})(this.paramLogger, this.dom, this.names);
this.chartLogger = ((logger, dom) => {
- let nodes = { 'gl-chart': dom.getElementsByClassName('gl-chart') };
+ const nodes = { 'gl-chart': dom.getElementsByClassName('gl-chart') };
return (i, chart, circularId) => {
let points = '';
- let len = chart.length;
- for (let i = 0; i < len; i++) {
- let id = (circularId + i + 1) % len;
- if (chart[id] != undefined) {
- points = points + ' ' + (55 * i / (len - 1)).toFixed(1) + ','
- + (45 - chart[id] * 22 / 60 / this.detected).toFixed(1);
- }
+ const len = chart.length;
+ for (let j = 0; j < len; j++) {
+ const id = (circularId + j + 1) % len;
+ if (chart[id] !== undefined) points = points + ' ' + (55 * j / (len - 1)).toFixed(1) + ',' + (45 - chart[id] * 22 / 60 / this.detected).toFixed(1);
}
nodes['gl-chart'][i].setAttribute('points', points);
logger(this.names[i], chart, circularId);
- }
+ };
})(this.chartLogger, this.dom);
}
}
/**
* Explicit UI add
- * @param { string | undefined } name
+ * @param { string | undefined } name
*/
addUI(name) {
- if (this.names.indexOf(name) == -1) {
+ if (this.names.indexOf(name) === -1) {
this.names.push(name);
if (this.dom) {
this.dom.insertAdjacentHTML('beforeend', this.svg);
@@ -186,21 +171,21 @@ class GLBench {
*/
nextFrame(now) {
this.frameId++;
- const t = now ? now : this.now();
+ const t = now || this.now();
// params
if (this.frameId <= 1) {
this.paramFrame = this.frameId;
this.paramTime = t;
} else {
- let duration = t - this.paramTime;
+ const duration = t - this.paramTime;
if (duration >= 1e3) {
const frameCount = this.frameId - this.paramFrame;
const fps = frameCount / duration * 1e3;
for (let i = 0; i < this.names.length; i++) {
- const cpu = this.cpuAccums[i] / duration * 100,
- gpu = this.gpuAccums[i] / duration * 100,
- mem = (performance && performance.memory) ? performance.memory.usedJSHeapSize / (1 << 20) : 0;
+ const cpu = this.cpuAccums[i] / duration * 100;
+ const gpu = this.gpuAccums[i] / duration * 100;
+ const mem = (performance && performance.memory) ? performance.memory.usedJSHeapSize / (1 << 20) : 0;
this.paramLogger(i, cpu, gpu, mem, fps, duration, frameCount);
this.cpuAccums[i] = 0;
Promise.all(this.finished).then(() => {
@@ -219,15 +204,13 @@ class GLBench {
this.chartTime = t;
this.circularId = 0;
} else {
- let timespan = t - this.chartTime;
+ const timespan = t - this.chartTime;
let hz = this.chartHz * timespan / 1e3;
while (--hz > 0 && this.detected) {
const frameCount = this.frameId - this.chartFrame;
const fps = frameCount / timespan * 1e3;
this.chart[this.circularId % this.chartLen] = fps;
- for (let i = 0; i < this.names.length; i++) {
- this.chartLogger(i, this.chart, this.circularId);
- }
+ for (let i = 0; i < this.names.length; i++) this.chartLogger(i, this.chart, this.circularId);
this.circularId++;
this.chartFrame = this.frameId;
this.chartTime = t;
@@ -253,7 +236,7 @@ class GLBench {
updateAccums(name) {
let nameId = this.names.indexOf(name);
- if (nameId == -1) {
+ if (nameId === -1) {
nameId = this.names.length;
this.addUI(name);
}
@@ -261,13 +244,11 @@ class GLBench {
const t = this.now();
const dt = t - this.t0;
for (let i = 0; i < nameId + 1; i++) {
- if (this.activeAccums[i]) {
- this.cpuAccums[i] += dt;
- }
- } this.activeAccums[nameId] = !this.activeAccums[nameId];
+ if (this.activeAccums[i]) this.cpuAccums[i] += dt;
+ }
+ this.activeAccums[nameId] = !this.activeAccums[nameId];
this.t0 = t;
}
-
}
export default GLBench;
diff --git a/demo/browser.js b/demo/browser.js
index 5170f69e..fbe8924c 100644
--- a/demo/browser.js
+++ b/demo/browser.js
@@ -35,9 +35,10 @@ const ui = {
menuWidth: 0,
menuHeight: 0,
camera: {},
- fps: [],
+ detectFPS: [],
+ drawFPS: [],
buffered: false,
- bufferedFPSTarget: 24,
+ bufferedFPSTarget: 0,
drawThread: null,
detectThread: null,
framesDraw: 0,
@@ -86,20 +87,21 @@ async function calcSimmilariry(faces) {
}
// draws processed results and starts processing of a next frame
+let lastDraw = performance.now();
async function drawResults(input) {
const result = lastDetectedResult;
const canvas = document.getElementById('canvas');
- // update fps data
- // const elapsed = performance.now() - timeStamp;
- if (result.performance && result.performance.total) ui.fps.push(1000 / result.performance.total);
- if (ui.fps.length > ui.maxFPSframes) ui.fps.shift();
+ // update draw fps data
+ ui.drawFPS.push(1000 / (performance.now() - lastDraw));
+ if (ui.drawFPS.length > ui.maxFPSframes) ui.drawFPS.shift();
+ lastDraw = performance.now();
// enable for continous performance monitoring
// console.log(result.performance);
// draw fps chart
- await menu.updateChart('FPS', ui.fps);
+ await menu.updateChart('FPS', ui.detectFPS);
// get updated canvas
if (ui.buffered || !result.canvas) result.canvas = await human.image(input, userConfig);
@@ -128,21 +130,26 @@ async function drawResults(input) {
const gpu = engine.backendInstance ? `gpu: ${(engine.backendInstance.numBytesInGPU ? engine.backendInstance.numBytesInGPU : 0).toLocaleString()} bytes` : '';
const memory = `system: ${engine.state.numBytes.toLocaleString()} bytes ${gpu} | tensors: ${engine.state.numTensors.toLocaleString()}`;
const processing = result.canvas ? `processing: ${result.canvas.width} x ${result.canvas.height}` : '';
- const avg = Math.trunc(10 * ui.fps.reduce((a, b) => a + b, 0) / ui.fps.length) / 10;
- const warning = (ui.fps.length > 5) && (avg < 5) ? 'warning: your performance is low: try switching to higher performance backend, lowering resolution or disabling some models' : '';
+ const avgDetect = Math.trunc(10 * ui.detectFPS.reduce((a, b) => a + b, 0) / ui.detectFPS.length) / 10;
+ const avgDraw = Math.trunc(10 * ui.drawFPS.reduce((a, b) => a + b, 0) / ui.drawFPS.length) / 10;
+ const warning = (ui.detectFPS.length > 5) && (avgDetect < 5) ? 'warning: your performance is low: try switching to higher performance backend, lowering resolution or disabling some models' : '';
document.getElementById('log').innerHTML = `
video: ${ui.camera.name} | facing: ${ui.camera.facing} | resolution: ${ui.camera.width} x ${ui.camera.height} ${processing}
backend: ${human.tf.getBackend()} | ${memory}
- performance: ${str(result.performance)} FPS:${avg}
+ performance: ${str(result.performance)} FPS process:${avgDetect} refresh:${avgDraw}
${warning}
`;
ui.framesDraw++;
ui.lastFrame = performance.now();
// if buffered, immediate loop but limit frame rate although it's going to run slower as JS is singlethreaded
- if (ui.buffered && !ui.drawThread) ui.drawThread = setInterval(() => drawResults(input, canvas), 1000 / ui.bufferedFPSTarget);
- // stop buffering
- if (!ui.buffered && ui.drawThread) {
+ if ((ui.bufferedFPSTarget === 0) && ui.buffered) {
+ ui.drawThread = requestAnimationFrame(() => drawResults(input, canvas));
+ } else if ((ui.bufferedFPSTarget === 0) && ui.buffered && !ui.drawThread) {
+ log('starting buffered refresh');
+ if (ui.bufferedFPSTarget > 0) ui.drawThread = setInterval(() => drawResults(input, canvas), 1000 / ui.bufferedFPSTarget);
+ } else if (!ui.buffered && ui.drawThread) {
+ log('stopping buffered refresh');
clearTimeout(ui.drawThread);
ui.drawThread = null;
}
@@ -156,7 +163,6 @@ async function setupCamera() {
const canvas = document.getElementById('canvas');
const output = document.getElementById('log');
const live = video.srcObject ? ((video.srcObject.getVideoTracks()[0].readyState === 'live') && (video.readyState > 2) && (!video.paused)) : false;
- console.log('camera live', live);
let msg = '';
status('setting up camera');
// setup webcam. note that navigator.mediaDevices requires that page is accessed via https
@@ -208,7 +214,6 @@ async function setupCamera() {
// silly font resizing for paint-on-canvas since viewport can be zoomed
const size = 14 + (6 * canvas.width / window.innerWidth);
ui.baseFont = ui.baseFontProto.replace(/{size}/, `${size}px`);
- console.log('camera continue', live);
if (live) video.play();
// eslint-disable-next-line no-use-before-define
if (live && !ui.detectThread) runHumanDetect(video, canvas);
@@ -229,6 +234,8 @@ function webWorker(input, image, canvas, timestamp) {
worker = new Worker(ui.worker, { type: 'module' });
// after receiving message from webworker, parse&draw results and send new frame for processing
worker.addEventListener('message', (msg) => {
+ if (msg.data.result.performance && msg.data.result.performance.total) ui.detectFPS.push(1000 / msg.data.result.performance.total);
+ if (ui.detectFPS.length > ui.maxFPSframes) ui.detectFPS.shift();
if (ui.bench) bench.end();
if (ui.bench) bench.nextFrame(timestamp);
lastDetectedResult = msg.data.result;
@@ -274,6 +281,8 @@ function runHumanDetect(input, canvas, timestamp) {
} else {
if (ui.bench) bench.begin();
human.detect(input, userConfig).then((result) => {
+ if (result.performance && result.performance.total) ui.detectFPS.push(1000 / result.performance.total);
+ if (ui.detectFPS.length > ui.maxFPSframes) ui.detectFPS.shift();
if (ui.bench) bench.end();
if (ui.bench) bench.nextFrame(timestamp);
if (result.error) log(result.error);
@@ -446,7 +455,7 @@ async function setupMonitor() {
bench = new GLBench(gl, {
trackGPU: true,
chartHz: 20,
- chartLen: 50,
+ chartLen: 20,
});
}
/*