












(function () {
  'use strict';

  var WINDOW_MS = 500;      

  function pct(arr, p) {
    if (!arr.length) return 0;
    var a = arr.slice().sort(function (x, y) { return x - y; });
    return a[Math.min(a.length - 1, Math.floor(a.length * p))];
  }

  function PerfHUD(vizScene, container) {
    this.scene = vizScene;
    this.visible = false;

    var el = document.createElement('div');
    el.id = 'perf-hud';
    el.style.cssText =
      'position:absolute; right:8px; bottom:8px; z-index:40; display:none;' +
      'font:11px/1.5 Consolas,Menlo,monospace; color:#cfd6dc;' +
      'background:rgba(20,24,28,.82); border:1px solid #39424b; border-radius:4px;' +
      'padding:6px 9px; white-space:pre; pointer-events:none; min-width:250px;';
    (container || document.body).appendChild(el);
    this._el = el;

    
    this._frameT = [];       
    this._renderMs = [];     
    this._cbMs = [];         
    this._pushes = 0;        
    this._winT0 = 0;
    this._cb0 = 0;
    this._machine = null;    
    this._env = '';          
    this._text = '';

    var self = this;
    
    this.scene.onFrameFirst(function () {
      if (self.visible) self._cb0 = performance.now();
    });
    this.scene.onFrame(function (now) {
      if (!self.visible) return;
      var t = performance.now();
      self._frameT.push(t);
      if (self._cb0) self._cbMs.push(t - self._cb0);
      if (t - self._winT0 >= WINDOW_MS) self._flush(t);
    });
  }

  
  PerfHUD.prototype.noteState = function (state) {
    if (!this.visible) return;
    this._pushes++;
    var m = state && state.motion;
    if (m && m.read_hz !== undefined) {
      this._machine = { hz: m.read_hz, drops: m.drops };
    }
  };

  PerfHUD.prototype.setVisible = function (on) {
    on = !!on;
    if (on === this.visible) return;
    this.visible = on;
    this._el.style.display = on ? 'block' : 'none';
    if (on) {
      this._readEnv();
      this._wrapRender();
      this._reset(performance.now());
      this._el.textContent = '采样中…';
    } else {
      this._unwrapRender();
      this._machine = null;
    }
  };

  PerfHUD.prototype.isVisible = function () { return this.visible; };

  

  var SOFT_RE = /swiftshader|software|llvmpipe|basic render|microsoft basic/i;

  
  PerfHUD.prototype._readEnv = function () {
    if (this._env) return;
    var gpu = '未知', accel = '?', backend = '', aa = '?';
    try {
      var gl = this.scene.renderer.getContext();
      var dbg = gl.getExtension('WEBGL_debug_renderer_info');
      if (dbg) gpu = String(gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) || '未知');
      
      var m = /^ANGLE \(([^,]+),\s*(.+?)(?:\s+vs_\d.*)?\)$/.exec(gpu);
      if (m) { gpu = m[2]; backend = 'ANGLE'; }
      accel = SOFT_RE.test(gpu) ? '✗ 软渲染' : '✓ 硬件加速';
      var ver = String(gl.getParameter(gl.VERSION) || '');
      backend = (backend ? backend + ' · ' : '') +
                (/WebGL 2/i.test(ver) ? 'WebGL2' : 'WebGL1');
      var at = gl.getContextAttributes && gl.getContextAttributes();
      
      if (at) aa = at.antialias ? '开' : '关';
    } catch (e) {  }
    this._gpuLine = accel + '   ' + gpu;
    this._env = backend + ' · 抗锯齿 ' + aa +
                ' · DPR ' + (window.devicePixelRatio || 1) +
                ' / PR ' + this.scene.renderer.getPixelRatio();
  };

  PerfHUD.prototype._wrapRender = function () {
    if (this._origRender) return;
    var r = this.scene.renderer, self = this;
    this._origRender = r.render.bind(r);
    r.render = function (a, b) {
      var t = performance.now();
      self._origRender(a, b);
      self._renderMs.push(performance.now() - t);
    };
  };

  PerfHUD.prototype._unwrapRender = function () {
    if (!this._origRender) return;
    this.scene.renderer.render = this._origRender;
    this._origRender = null;
  };

  PerfHUD.prototype._reset = function (t) {
    this._winT0 = t;
    this._frameT.length = 0;
    this._renderMs.length = 0;
    this._cbMs.length = 0;
    this._pushes = 0;
  };

  PerfHUD.prototype._flush = function (t) {
    var span = (t - this._winT0) / 1000;
    var n = this._frameT.length;
    var fps = span > 0 ? n / span : 0;

    
    var gaps = [];
    for (var i = 1; i < n; i++) gaps.push(this._frameT[i] - this._frameT[i - 1]);

    var st = this.scene.frameStats || {};
    var budget = [60, 30, 20][st.tier || 0];
    var info = (this.scene.renderer.info && this.scene.renderer.info.render) || {};
    var nTargets = (window.viz && window.viz.targetsUI &&
                    window.viz.targetsUI.items.length) || 0;

    var lines = [
      '渲染   ' + fps.toFixed(1) + ' fps   档位 ' + budget + 'fps   迟到 ' +
        Math.round((st.lateRatio || 0) * 100) + '%',
      '帧间隔 ' + pct(gaps, 0.5).toFixed(1) + ' / ' + pct(gaps, 0.9).toFixed(1) +
        ' ms   (中位/p90)',
      '耗时   回调 ' + pct(this._cbMs, 0.5).toFixed(2) + '   渲染 ' +
        pct(this._renderMs, 0.5).toFixed(2) + ' ms   (中位)',
      '场景   ' + (info.calls || 0) + ' draw   ' +
        ((info.triangles || 0) / 1000).toFixed(1) + 'k 面   目标点 ' + nTargets,
      '数据   推送 ' + (this._pushes / span).toFixed(0) + ' Hz' +
        (this._machine
          ? '   真机 ' + this._machine.hz.toFixed(0) + ' Hz   丢帧 ' + this._machine.drops
          : '   （真机数据仅示教期有）'),
      'GPU    ' + this._gpuLine,
      '上下文 ' + this._env
    ];
    var text = lines.join('\n');
    if (text !== this._text) {      
      this._text = text;
      this._el.textContent = text;
    }
    this._reset(t);
  };

  window.PerfHUD = PerfHUD;
})();
