从Cesium的源码,自定义帧率显示

959 阅读2分钟

FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数。FPS是测量用于保存、显示动态视频的信息数量。每秒钟帧数越多,所显示的动作就会越流畅。通常,要避免动作不流畅的最低是30。FPS也可以理解为我们常说的“刷新率(单位为Hz)。帧率的显示主要是为了我们对性能的调试。

在Cesium中自带帧率FPS控件。但是其默认显示属性是false。启用该控件可设置属性为true。

viewer.scene.debugShowFramesPerSecond = true

QQ截图20230605160354.png

以上图片是开启FPS控件,默认展示样式。有时候基于业务需求,展示形式可能需要更改。查看Cesium源码,对PFS的值的计算。源代码在目录Source\Scene\PerformanceDisplay.js下。主要是原型链上update的计算方法。

import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
import getTimestamp from "../Core/getTimestamp.js";
import getElement from "../Widgets/getElement.js";

/**
 * @private
 */
function PerformanceDisplay(options) {
  options = defaultValue(options, defaultValue.EMPTY_OBJECT);

  var container = getElement(options.container);
  //>>includeStart('debug', pragmas.debug);
  if (!defined(container)) {
    throw new DeveloperError("container is required");
  }
  //>>includeEnd('debug');

  this._container = container;

  var display = document.createElement("div");
  display.className = "cesium-performanceDisplay";
  var fpsElement = document.createElement("div");
  fpsElement.className = "cesium-performanceDisplay-fps";
  this._fpsText = document.createTextNode("");
  fpsElement.appendChild(this._fpsText);
  var msElement = document.createElement("div");
  msElement.className = "cesium-performanceDisplay-ms";
  this._msText = document.createTextNode("");
  msElement.appendChild(this._msText);
  display.appendChild(msElement);
  display.appendChild(fpsElement);
  this._container.appendChild(display);

  this._lastFpsSampleTime = getTimestamp();
  this._lastMsSampleTime = getTimestamp();
  this._fpsFrameCount = 0;
  this._msFrameCount = 0;

  this._throttled = false;
  var throttledElement = document.createElement("div");
  throttledElement.className = "cesium-performanceDisplay-throttled";
  this._throttledText = document.createTextNode("");
  throttledElement.appendChild(this._throttledText);
  display.appendChild(throttledElement);
}

Object.defineProperties(PerformanceDisplay.prototype, {
  /**
   * The display should indicate the FPS is being throttled.
   * @memberof PerformanceDisplay.prototype
   *
   * @type {Boolean}
   */
  throttled: {
    get: function () {
      return this._throttled;
    },
    set: function (value) {
      if (this._throttled === value) {
        return;
      }

      if (value) {
        this._throttledText.nodeValue = "(throttled)";
      } else {
        this._throttledText.nodeValue = "";
      }

      this._throttled = value;
    },
  },
});

/**
 * Update the display.  This function should only be called once per frame, because
 * each call records a frame in the internal buffer and redraws the display.
 *
 * @param {Boolean} [renderedThisFrame=true] If provided, the FPS count will only update and display if true.
 */
PerformanceDisplay.prototype.update = function (renderedThisFrame) {
  var time = getTimestamp();
  var updateDisplay = defaultValue(renderedThisFrame, true);

  this._fpsFrameCount++;
  var fpsElapsedTime = time - this._lastFpsSampleTime;
  if (fpsElapsedTime > 1000) {
    var fps = "N/A";
    if (updateDisplay) {
      fps = ((this._fpsFrameCount * 1000) / fpsElapsedTime) | 0;
    }

    this._fpsText.nodeValue = fps + " FPS";
    this._lastFpsSampleTime = time;
    this._fpsFrameCount = 0;
  }

  this._msFrameCount++;
  var msElapsedTime = time - this._lastMsSampleTime;
  if (msElapsedTime > 200) {
    var ms = "N/A";
    if (updateDisplay) {
      ms = (msElapsedTime / this._msFrameCount).toFixed(2);
    }

    this._msText.nodeValue = ms + " MS";
    this._lastMsSampleTime = time;
    this._msFrameCount = 0;
  }
};

/**
 * Destroys the WebGL resources held by this object.
 */
PerformanceDisplay.prototype.destroy = function () {
  return destroyObject(this);
};
export default PerformanceDisplay;

从代码中可以看出,想要自定义帧率显示的样式,有两种方式。 1.修改原来的样式

QQ截图20230606102039.png

2.更改源码

let getTimestamp

if (typeof performance !== 'undefined' && typeof performance.now === 'function' && isFinite(performance.now())) {
  getTimestamp = function() {
    return performance.now()
  }
} else {
  getTimestamp = function() {
    return Date.now()
  }
}

class CustomPerformanceDisplay {
  constructor() {
    this._lastFpsSampleTime = getTimestamp()
    this._lastMsSampleTime = getTimestamp()
    this._fpsFrameCount = 0
    this._msFrameCount = 0
    this.createDiv()
  }

  createDiv() {
    const parent = document.createElement('div')
    const fpsDiv = document.createElement('div')
    fpsDiv.className = 'info-wrap'

    this._fpsText = document.createElement('span')
    this._msText = document.createElement('span')
    fpsDiv.append(this._fpsText)
    fpsDiv.append(this._msText)
    parent.append(fpsDiv)
  }

  update() {
    let time = getTimestamp()
    this._fpsFrameCount++
    let updateDisplay = true
    let fpsElapsedTime = time - this._lastFpsSampleTime
    if (fpsElapsedTime > 1000) {
      let fps = 'N/A'
      if (updateDisplay) {
        fps = ((this._fpsFrameCount * 1000) / fpsElapsedTime) | 0
      }

      this._fpsText.innerText = fps + ' FPS'
      this._lastFpsSampleTime = time
      this._fpsFrameCount = 0
    }
    this._msFrameCount++
    let msElapsedTime = time - this._lastMsSampleTime
    if (msElapsedTime > 200) {
      let ms = 'N/A'
      if (updateDisplay) {
        ms = (msElapsedTime / this._msFrameCount).toFixed(2)
      }

      this._msText.innerText = ms + ' MS'
      this._lastMsSampleTime = time
      this._msFrameCount = 0
    }
  }
}

export default CustomPerformanceDisplay

在项目中使用

const fpsInfo = new CustomPerformanceDisplay()
const fpsPostEvent = () => {
  fpsInfo.update()
}
viewer.scene.postRender.addEventListener(fpsPostEvent)