我想用 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/