(function (global) { 'use strict'; /** * Ring-buffer performance sampler reading cc.profiler.stats. * @constructor */ function PerfSampler() { this.intervalMs = 200; this.capacity = 300; this.paused = false; this.times = []; this.series = { fps: [], frame: [], draws: [], tricount: [], logic: [], physics: [], render: [], present: [], textureMemory: [], bufferMemory: [], jsHeap: [], assetCount: [], }; this.markers = []; this._timer = 0; this._cc = null; this._onSample = null; this._userMarkSeq = 0; } /** @param {*} cc */ PerfSampler.prototype.bindEngine = function (cc) { this._cc = cc; // 不在绑定时强制 showStats:引擎初始化未完成时会触发 profiler null 报错 }; /** @param {function():void} cb */ PerfSampler.prototype.onSample = function (cb) { this._onSample = cb; }; /** Start interval sampling. */ PerfSampler.prototype.start = function () { var self = this; this.stop(); this._timer = setInterval(function () { self.sample(); }, this.intervalMs); }; /** Stop sampling timer. */ PerfSampler.prototype.stop = function () { if (this._timer) { clearInterval(this._timer); this._timer = 0; } }; /** * Set capacity for time window. * @param {number} n */ PerfSampler.prototype.setCapacity = function (n) { this.capacity = n; this._trim(); }; /** * Set sample interval ms. * @param {number} ms */ PerfSampler.prototype.setIntervalMs = function (ms) { this.intervalMs = ms; if (this._timer) this.start(); }; /** Clear series and markers. */ PerfSampler.prototype.clear = function () { this.times.length = 0; for (var k in this.series) { if (Object.prototype.hasOwnProperty.call(this.series, k)) this.series[k].length = 0; } this.markers.length = 0; this._userMarkSeq = 0; }; /** * Push marker at now. * @param {string} label */ PerfSampler.prototype.mark = function (label) { this.markers.push({ t: performance.now(), label: String(label || 'mark') }); if (this.markers.length > 200) this.markers.splice(0, this.markers.length - 200); if (this._onSample) this._onSample(); }; /** User mark from UI. */ PerfSampler.prototype.userMark = function (text) { this._userMarkSeq += 1; var label = text && String(text).trim() ? String(text).trim() : ('user:' + this._userMarkSeq); this.mark(label); }; /** Take one sample. */ PerfSampler.prototype.sample = function () { if (this.paused) return; var cc = this._cc; var now = performance.now(); var stats = cc && cc.profiler ? cc.profiler.stats : null; function read(key) { if (!stats || !stats[key] || !stats[key].counter) return 0; var v = stats[key].counter.value; return typeof v === 'number' ? v : 0; } this.times.push(now); this.series.fps.push(read('fps')); this.series.frame.push(read('frame')); this.series.draws.push(read('draws')); this.series.tricount.push(read('tricount')); this.series.logic.push(read('logic')); this.series.physics.push(read('physics')); this.series.render.push(read('render')); this.series.present.push(read('present')); this.series.textureMemory.push(read('textureMemory')); this.series.bufferMemory.push(read('bufferMemory')); var heap = 0; if (performance.memory && performance.memory.usedJSHeapSize) { heap = performance.memory.usedJSHeapSize / (1024 * 1024); } this.series.jsHeap.push(heap); var assetCount = 0; if (cc && cc.assetManager && cc.assetManager.assets) { try { cc.assetManager.assets.forEach(function () { assetCount += 1; }); } catch (e) { assetCount = 0; } } this.series.assetCount.push(assetCount); this._trim(); if (this._onSample) this._onSample(); }; /** Latest scalar snapshot for strip/summary. */ PerfSampler.prototype.latest = function () { function last(arr) { return arr.length ? arr[arr.length - 1] : 0; } return { fps: last(this.series.fps), frame: last(this.series.frame), draws: last(this.series.draws), textureMemory: last(this.series.textureMemory), bufferMemory: last(this.series.bufferMemory), jsHeap: last(this.series.jsHeap), assetCount: last(this.series.assetCount), }; }; /** Export CSV string. */ PerfSampler.prototype.toCsv = function () { var keys = Object.keys(this.series); var lines = ['t,' + keys.join(',')]; for (var i = 0; i < this.times.length; i++) { var row = [this.times[i]]; for (var k = 0; k < keys.length; k++) row.push(this.series[keys[k]][i]); lines.push(row.join(',')); } if (this.markers.length) { lines.push(''); lines.push('marker_t,label'); for (var m = 0; m < this.markers.length; m++) { lines.push(this.markers[m].t + ',' + JSON.stringify(this.markers[m].label)); } } return lines.join('\n'); }; PerfSampler.prototype._trim = function () { while (this.times.length > this.capacity) { this.times.shift(); for (var k in this.series) { if (Object.prototype.hasOwnProperty.call(this.series, k)) this.series[k].shift(); } } if (this.times.length) { var t0 = this.times[0]; while (this.markers.length && this.markers[0].t < t0 - 1000) this.markers.shift(); } }; global.PWPerfSampler = PerfSampler; })(window);