2013-03-22 11 views
5

私の2dゲームエンジン用のキャンバスベースのレンダリングを書き直そうとしています。私は良い進歩を遂げており、スケーリング、ローテーション、ブレンディングで完結したwebglコンテキストにテクスチャを細かくレンダリングできます。しかし、私のパフォーマンスはうんざりです。私のテストラップトップでは、バニラの2Dキャンバスで30フレーム/秒、画面上に1,000個のエンティティを一度に取得できます。 WebGLでは、画面上に500個のエンティティを持つ30 fpsを取得します。状況が逆転すると思うよ!WebGLでバッチコールを行う最も速い方法

私は犯人がこのすべてであることを疑っている疑いがあります。Float32Array私は投げ捨てています。ここに私のレンダリングコードは次のとおりです。

// 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; 
} 

より良いパフォーマンスを取得する方法上の任意のアイデア:

// 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); 
}, 

そして、ここでは私の簡単なテストシェーダ(これらは&回転スケーリング、ブレンドが欠落している)ですか?私のdrawArraysをバッチする方法はありますか?バッファーゴミを減らす方法はありますか?

ありがとうございます!

+1

もっと知りたい場合は、「WebGLでdrawArraysをバッチ処理する最速の方法」 –

+0

@Mikko Doneのようにタイトルを付けることをお勧めします。ありがとう! –

+0

@AbrahamWalters:フレームごとにエンティティごとに正確なレンダーコードを実行していますか? (別名500 * 30 = 1500回/秒)もしそうなら、タブ/ブラウザはただそこに座らせると1時間以内に(10分ではないにしても)メモリが使い果たされると思います。 –

答えて

7

パフォーマンスに悪影響を与える2つの大きな問題があります。

一時的なFloat32Arraysをたくさん作成していますが、これは現在は高価です(将来的には改善するはずです)。はるかに大きな問題は、しかし、あなたは一度に単一のクワッドを描画しているということです

verts[0] = left; verts[1] = top; 
verts[2] = right; verts[3] = top; 
// etc... 
gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW); 

:これは、単一のアレイを作成するには、この場合にははるかに良くなるので、同様に頂点を毎回設定します。 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(); 
} 

単純化し過ぎだし、バッチへの方法は、さらにそこだが、うまくいけば、それはあなたのパフォーマンス向上のため、あなたの呼び出しをバッチ処理を開始する方法についてのアイデアを与えること。

+0

これはかなり意味があります。マップタイルやパーティクルのようなものは確かにまとめることができますが、終わりにはフレームごとにテクスチャをたくさんバインドします。テクスチャアトラスなしでこれを回避する方法はありますか? –

2

WebGLインスペクタを使用すると、不必要なGL命令(明るい黄色の背景でマークされています)を実行するかどうかトレースに表示されます。これにより、レンダリングを最適化する方法がわかります。

一般に、描画呼び出しを並べ替えると、すべて同じプログラムを使用し、次に属性、テクスチャ、次にユニフォームが順番に実行されます。この方法でGL命令(とJS命令)をできるだけ少なくします。

関連する問題