请帮助我理解为什么在 2D 上下文中在 HTML 画布上渲染的过程需要这么长时间,并且有多个帧从动画中掉出,并且有大量的点。此外,根据开发者工具的“性能”选项卡,JavaScript代码的执行时间大幅落在分配的16毫秒内,并且主要动画时间被GPU的工作占用。我会提前表明我正在 Google Chrome v122.0.6261.111 浏览器上进行测试。
这是我的示例代码
function main() {
const canvas = document.getElementById('canvas')
if (!canvas) return
canvas.width = window.innerWidth
canvas.height = window.innerHeight
const ctx = canvas.getContext('2d')
if (!ctx) return
const length = 20000
const xStep = canvas.width / length
const diapason = 50
const datasetsQty = 7
const datasets = new Array(datasetsQty).fill(undefined).map((item, index) => {
const bottom = index * 100
return generateRandomDataset(length, [bottom, bottom + diapason], xStep)
})
ctx.strokeStyle = 'red'
ctx.lineWidth = 2
requestAnimationFrame(() => {
renderer(ctx, datasets)
})
}
requestAnimationFrame(() => {
main()
})
/**
* global Y offset
*/
let count = 0
/**
* build one line per dataset with global Y offset
*/
function renderer(ctx, datasets) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
ctx.beginPath()
for (let dataset of datasets) {
for (let i = 0; i < dataset.length; i++) {
if (!i) ctx.moveTo(dataset[i][0], dataset[i][1] + count)
else ctx.lineTo(dataset[i][0], dataset[i][1] + count)
}
}
ctx.stroke()
count++
count %= window.innerHeight
requestAnimationFrame(() => {
renderer(ctx, datasets)
})
}
/**
* generate random dataset in given Y coord range and X is incrementing value
* [[0, random], [1, random], [n, random]...]
*/
function generateRandomDataset(length, range = [0, 50], xStep) {
const arr = []
let x = 0
for (let i = 0; i < length; i++) {
arr.push([
x,
Math.random() * (range[1] - range[0]) + range[0],
])
x += xStep
}
return arr
}
<canvas id="canvas"></canvas>
这里我已经想象出了这个问题。简而言之,有几个数据集(具体为 7 个)。在每个后续帧中,我绘制所有数据集,其 Y 轴偏移量比前一帧低一个像素。 该代码非常原始 - 没有矩阵或路径对象。然而,使用矩阵等并不会影响问题——JavaScript代码发送数据进行渲染所需的时间减少了,但GPU的运行时间与这个事实几乎没有相关性。
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
ctx.beginPath()
for (let dataset of datasets) {
for (let i = 0; i < dataset.length; i++) {
if (!i) ctx.moveTo(dataset[i][0], dataset[i][1] + count)
else ctx.lineTo(dataset[i][0], dataset[i][1] + count)
}
}
ctx.stroke()
我会给你一些截图来帮助你理解我们在说什么。 是第一帧的性能测量。请注意,javascript代码执行时间仅仅超过1ms,而GPU加载时间已经超过50ms!当然,一些动画帧会被丢弃。 当数据集超出画布时,情况就会发生变化 - JavaScript 代码的执行时间不会改变,GPU 加载时间也会相应减少。例如, 是画布上具有一个数据集的最后一帧,以及显示所有数据集的后续帧。正如您所看到的,一旦我们必须再次绘制可见区域中的所有数据集,图形处理器就会立即陷入负载,再次导致帧丢失。
几个事实增加了这个问题的兴趣:
问题本质上已经包含在开头了。为什么 GPU 上有如此巨大的负载以及如何避免它?
提前谢谢您,我真的希望有人可以帮助我解决提出的问题。