javascript - 如何优化 60fps 模拟器的 Canvas 性能?

标签 javascript performance html5-canvas

我想用 JavaScript 编写一个 gameboy 模拟器。屏幕分辨率为160 x 144 。它有四种颜色:黑色、深灰色、浅灰色和白色。 Gameboy 的屏幕刷新率为 60fps。

我有一个screen大小为 160*144=23040 的数组,其中每个索引存储 0-3 的颜色阴影。

为了向用户呈现屏幕,我想使用 HTML5 Canvas 。 据我了解,最直接的方法是创建一个 ImageData 带有 Uint8ClampedArray 的对象RGBA 位图并通过 putImageData 将其传递到 Canvas .

为了做到这一点,我必须将屏幕的每一帧转换为 Uint8ClampedArray ,并将每个阴影转换为其各自的 4 字节 RGBA 数字。这个新的 RGBA 位图将是 (160 * 144 * 4 bytes per pixel = 92160尺寸:

function screenToBuffer(screen) {
  return Uint8ClampedArray.from(screen.map(shade => shadeToRGBA(shade)).flat());
}

然后,该缓冲区被传递给用户提供的回调函数,在该函数中,他们会将其显示到 Canvas 上,如下所示:

onFrame: function(frameBuffer) {
  var image = new ImageData(frameBuffer, 160, 144)
  ctx.putImageData(image, 0, 0);
}

这在理论上工作得很好,但这是一种幼稚的方法,我发现它根本没有效率。通过使用探查器,screenToBuffer仅此功能就需要约 25 毫秒。如果屏幕要以 60fps 更新,则需要每 16.6ms 渲染一次!

解决这个问题的最佳方法是什么?创建一个全新的 92kb Uint8ClampedArray每帧映射 23,000 种色调的成本太高。

我想保留screen数组,因为它比 Uint8ClampedArray 更小,并且在我的代码中更容易推理。 。我想最好初始化一个 Uint8ClampedArray一次,并在 screen 发生更改时更新它大批。这样我就可以简单地返回每个帧上的 RGBA 缓冲区,并且它应该已经与我的屏幕同步。

我还想知道是否创建一个新的 92kb ImageData每个帧的对象也是资源密集型的,是否有更好的方法来处理这个问题。

有什么建议吗?

最佳答案

捆绑绘制调用并提前保存调色板,您对此应该不会有太多问题。

看看下面我的代码片段:

//Generate output node for time
var output = document.body.appendChild(document.createElement("p"));
//Generate canvas
var canvas = document.body.appendChild(document.createElement("canvas"));
var ctx = canvas.getContext("2d");
canvas.style.width = "500px";
//Setup canvas
canvas.width = 160;
canvas.height = 144;
//Generate color pallette
var colors = ["black", "#444", "#ccc", "white"]
    .map(function (b) {
    ctx.fillStyle = b;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    return ctx.getImageData(0, 0, 1, 1).data;
});
//Generate framedata (referencing back to one of our 4 base colors)
var data = [];
while (data.length < canvas.width * canvas.height) {
    data.push(Math.floor(Math.random() * 4));
}
//draw function
function drawCanvas() {
    //Start time
    var t = Date.now();
    //Fill with basic color
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    //Prepare ImageData
    var frame = new ImageData(canvas.width, canvas.height);
    //Loop through buffer
    for (var i = 0; i < data.length; i++) {
        var dataPoint = data[i];
        //Skip base color
        if (dataPoint == 0) {
            continue;
        }
        //Set color from palette
        frame.data.set(colors[dataPoint], i * 4);
    }
    //Put ImageData on canvas
    ctx.putImageData(frame, 0, 0);
    //Output time
    output.textContent = (Date.now() - t).toFixed(2);
    //Schedule next frame
    requestAnimationFrame(drawCanvas);
}
//Start drawing
drawCanvas();
//Start simulation at 60 fps
setInterval(function () {
    data = data.map(function () { return Math.floor(Math.random() * 4); });
}, 16);

我每帧持续大约 2 毫秒。

关于javascript - 如何优化 60fps 模拟器的 Canvas 性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53067088/

相关文章:

javascript - 点击监听器内的发布请求未重定向到请求的页面

c - 内存到内存传输优化,如何不memcpy?

html - 使用 HTML5 Canvas 文本 - SEO 后果

javascript - requestanimationframe drawing with for loop 问题

javascript - React - 以任何顺序从数组中过滤项目

javascript - 我可以在 javaScript 和 typeScript 中为 firebase 编写云函数吗?

javascript - 随滚动变化的动画粘性标题

c# - 测量每个 Entity Framework 查询的时间

Java 的 FilterOutputStream 增加了性能损失

javascript - setTimeout 只运行一次