Abr*_*ers 7 javascript performance webgl html5-canvas 2d-games
我正在尝试为我的2D游戏引擎重写基于画布的渲染.我已经取得了很好的进展,可以很好地渲染纹理到webgl上下文,完成缩放,旋转和混合.但我的表现很糟糕.在我的测试笔记本电脑上,我可以获得30 fps的vanilla 2d画布,同时屏幕上有1,000个实体; 在WebGL中,我在屏幕上获得了30个fps,500个实体.我希望情况会逆转!
我有一种潜在的怀疑,罪魁祸首就是Float32Array我正在折腾的所有缓冲垃圾.这是我的渲染代码:
// boilerplate code and obj coordinates
// grab gl context
var canvas = sys.canvas;
var gl = sys.webgl;
var program = sys.glProgram;
// width and height
var scale = sys.scale;
var tileWidthScaled = Math.floor(tileWidth * scale);
var tileHeightScaled = Math.floor(tileHeight * scale);
var normalizedWidth = tileWidthScaled / this.width;
var normalizedHeight = tileHeightScaled / this.height;
var worldX = targetX * scale;
var worldY = targetY * scale;
this.bindGLBuffer(gl, this.vertexBuffer, sys.glWorldLocation);
this.bufferGLRectangle(gl, worldX, worldY, tileWidthScaled, tileHeightScaled);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
var frameX = (Math.floor(tile * tileWidth) % this.width) * scale;
var frameY = (Math.floor(tile * tileWidth / this.width) * tileHeight) * scale;
// fragment (texture) shader
this.bindGLBuffer(gl, this.textureBuffer, sys.glTextureLocation);
this.bufferGLRectangle(gl, frameX, frameY, normalizedWidth, normalizedHeight);
gl.drawArrays(gl.TRIANGLES, 0, 6);
bufferGLRectangle: function (gl, x, y, width, height) {
var left = x;
var right = left + width;
var top = y;
var bottom = top + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
left, top,
right, top,
left, bottom,
left, bottom,
right, top,
right, bottom
]), gl.STATIC_DRAW);
},
bindGLBuffer: function (gl, buffer, location) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);
},
Run Code Online (Sandbox Code Playgroud)
这是我的简单测试着色器(缺少混合,缩放和旋转):
// fragment (texture) shader
precision mediump float;
uniform sampler2D image;
varying vec2 texturePosition;
void main() {
gl_FragColor = texture2D(image, texturePosition);
}
// vertex shader
attribute vec2 worldPosition;
attribute vec2 vertexPosition;
uniform vec2 canvasResolution;
varying vec2 texturePosition;
void main() {
vec2 zeroToOne = worldPosition / canvasResolution;
vec2 zeroToTwo = zeroToOne * 2.0;
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
texturePosition = vertexPosition;
}
Run Code Online (Sandbox Code Playgroud)
关于如何获得更好性能的任何想法?有没有办法批量我的drawArrays?有没有办法减少缓冲垃圾?
谢谢!
我在这里看到的两个大问题会对您的表现产生不利影响.
你正在创建许多临时的Float32Arrays,它们目前构建成本很高(未来应该会变得更好).在这种情况下,创建单个数组并每次设置顶点会更好:
verts[0] = left; verts[1] = top;
verts[2] = right; verts[3] = top;
// etc...
gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
Run Code Online (Sandbox Code Playgroud)
然而,到目前为止,更大的问题是你一次只能绘制一个四边形.3D API并非旨在有效地实现此目的.你想要做的是尝试在每个drawArrays/drawElements调用中挤出尽可能多的三角形.
有几种方法可以做到这一点,最简单的方法就是用尽可能多的四边形填充缓冲区来共享相同的纹理,然后一次性绘制它们.在伪代码中:
var MAX_QUADS_PER_BATCH = 100;
var VERTS_PER_QUAD = 6;
var FLOATS_PER_VERT = 2;
var verts = new Float32Array(MAX_QUADS_PER_BATCH * VERTS_PER_QUAD * FLOATS_PER_VERT);
var quadCount = 0;
function addQuad(left, top, bottom, right) {
var offset = quadCount * VERTS_PER_QUAD * FLOATS_PER_VERT;
verts[offset] = left; verts[offset+1] = top;
verts[offset+2] = right; verts[offset+3] = top;
// etc...
quadCount++;
if(quadCount == MAX_QUADS_PER_BATCH) {
flushQuads();
}
}
function flushQuads() {
gl.bindBuffer(gl.ARRAY_BUFFER, vertsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW); // Copy the buffer we've been building to the GPU.
// Make sure vertexAttribPointers are set, etc...
gl.drawArrays(gl.TRIANGLES, 0, quadCount + VERTS_PER_QUAD);
}
// In your render loop
for(sprite in spriteTypes) {
gl.bindTexture(gl.TEXTURE_2D, sprite.texture);
for(instance in sprite.instances) {
addQuad(instance.left, instance.top, instance.right, instance.bottom);
}
flushQuads();
}
Run Code Online (Sandbox Code Playgroud)
这是一种过于简单化的方法,而且还有更多方法可以批量处理,但希望能让您了解如何开始批量调用以获得更好的性能.