You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

three.js中如何等待<canvas>完成渲染更新以获取真实FPS?

Three.js中如何准确测量真实可见帧的FPS?

在requestAnimationFrame()循环中调用renderer.render()时,其触发速度往往快于<canvas>元素更新图像的速度,导致真实FPS测量结果不准确。

比如在卡顿的Three.js场景中,若在页面HTML元素显示帧号,用高帧率(约120Hz)录制屏幕会发现:画布仅显示2张不同图像,但帧号从1340跳到1343,实际跳过了2帧,这会虚高FPS测量值——本质是requestAnimationFrame()的触发速度快于浏览器重绘<canvas>及页面其他元素的速度。

我已尝试多种方案,但效果不佳:

  • 将<canvas>连接到<video>元素,利用video.requestVideoFrameCallback(callback):这是唯一能阻止跳帧的方法,但会引入更严重的卡顿,得不偿失。
  • 使用MessageChannel方案:无效果。
  • 使用requestAnimationFrame(() => requestAnimationFrame(callback)):无效果。

因此,是否存在可靠的方法,阻塞requestAnimationFrame()的每次迭代,让画布及页面所有元素完成重绘?以此获取屏幕上实际可见帧的真实FPS测量值。

补充说明:修改场景中InstancedMesh的实例数量(如const mesh = new THREE.InstancedMesh(geometry, material, 100);)可让场景卡顿,高帧率录制屏幕并快速旋转相机时,能看到帧号跳变(截图中可见1022和1029帧被跳过)。


解决方案

要准确测量屏幕上实际可见帧的FPS,核心是等待浏览器完成真实的像素绘制后再触发下一帧渲染,以下是两种可靠方案:

1. 使用requestPostAnimationFrame(Chrome 104+)

这是浏览器原生提供的API,会在浏览器完成当前帧的像素绘制后触发回调,完美匹配“等待重绘完成”的需求。

修改你的渲染循环:

function render() {
  // 执行场景更新、渲染操作
  renderer.render(scene, camera);
  // 更新帧号等UI元素
  updateFrameCounter();
  
  // 等待浏览器完成重绘后再触发下一帧
  requestPostAnimationFrame(render);
}

// 启动循环
requestPostAnimationFrame(render);

这个API直接绑定浏览器的绘制流程,能确保每一次渲染都对应屏幕上的一帧可见画面,不会出现帧号跳变的情况。

2. 基于PerformanceObserver监听绘制标记(兼容更广)

如果需要兼容不支持requestPostAnimationFrame的浏览器,可以通过监听浏览器的绘制性能标记来判断重绘完成:

let isRendering = false;
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.name === 'mark-render-end' && !isRendering) {
      isRendering = true;
      requestAnimationFrame(render);
    }
  }
});
observer.observe({ entryTypes: ['mark'] });

function render() {
  isRendering = false;
  // 场景更新与渲染
  renderer.render(scene, camera);
  updateFrameCounter();
  // 标记渲染结束,等待PerformanceObserver监听绘制完成
  performance.mark('mark-render-end');
}

// 启动循环
requestAnimationFrame(render);

原理是在渲染完成后打一个性能标记,当浏览器完成绘制后,PerformanceObserver会捕获到相关的绘制事件,此时再触发下一帧渲染,确保每一次渲染都对应一次真实的屏幕更新。

关键注意点

  • 不要尝试用同步阻塞的方式(如setTimeout、while循环)来等待重绘,这会导致浏览器主线程卡死,反而加剧卡顿。
  • 上述方案都是异步等待浏览器的绘制完成信号,不会阻塞主线程,同时能保证帧号与实际可见帧一一对应,从而得到准确的FPS数值。

内容的提问来源于stack exchange,提问作者user3163495

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 05:40:03