2013-03-22 19 views
5

Próbuję przepisać renderowanie oparte na płótnie dla mojego silnika gier 2d. Zrobiłem duży postęp i mogę renderować tekstury do kontekstu webgl w porządku, wraz ze skalowaniem, rotacją i mieszaniem. Ale mój występ jest do dupy. Na moim testowym laptopie mogę uzyskać 30 fps na płótnie 2-osobowym wanilii z 1000 elementów na ekranie jednocześnie; w WebGL uzyskuję 30 fps z 500 elementami na ekranie. Spodziewałbym się, że sytuacja się odwróci!Najszybszy sposób na wywoływanie wsadowe w WebGL

Mam podstępne podejrzenie, że winowajcą jest wszystko to Float32Array śmieci buszowych, które rzucam. Oto mój kod renderowania:

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

A oto moje proste shadery badań (są one brakuje mieszania, skalowanie & obrót):

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

jakieś pomysły w jaki sposób uzyskać lepszą wydajność? Czy istnieje sposób na grupowanie moich drawArrays? Czy istnieje sposób na zmniejszenie ilości śmieci buforowych?

Dzięki!

+1

Aby przyciągnąć więcej osób z intencją, proponuję tytułować pytanie: "Najszybszy sposób na grupowanie drawArrays w WebGL" –

+0

@Mikko Done. Dzięki! –

+0

@AbrahamWalters: czy uruchomiłeś dokładnie ten kod renderowania dla każdej jednostki dla każdej klatki? (aka 500 * 30 = 1500 razy na sekundę) Jeśli tak, myślę, że karta/przeglądarka zabraknie pamięci w ciągu godziny (jeśli nie dziesięć minut), jeśli pozwolisz mu po prostu usiąść. –

Odpowiedz

7

Widzę tu dwa duże problemy, które mogą negatywnie wpłynąć na wydajność.

Tworzysz wiele tymczasowych Float32Arrays, które są obecnie drogie w budowie (które powinny poprawić się w przyszłości). Byłoby znacznie lepiej w tym przypadku stworzenie jednolitego i ustawić tablicę wierzchołków za każdym razem tak:

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

Większym problemem zdecydowanie jest jednak to, że jesteś rysunek tylko jeden quad naraz . Interfejsy API 3D po prostu nie są zaprojektowane, aby robić to skutecznie. To, co chcesz zrobić, to spróbować wycisnąć jak najwięcej trójkątów do każdego wywołania drawArrays/drawElements.

Jest kilka sposobów na zrobienie tego, najprostszą z nich jest wypełnienie bufora tak wieloma quadami, jak to tylko możliwe, które mają tę samą teksturę, a następnie narysowanie ich za jednym razem. W psuedocode:

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(); 
} 

To nadmierne uproszczenie, a tam sposoby partii nawet więcej, ale mam nadzieję, że daje wyobrażenie o tym, jak zacząć dozowania połączeń dla lepszej wydajności.

+0

To ma wiele sensu. Z całą pewnością mogę wsypywać rzeczy takie jak kafelki mapy i cząstki, ale pod koniec dnia będę wiązał tekstury z każdą klatką. Czy jest jakiś sposób obejścia tego bez atlasu tekstury? –

2

Jeśli korzystasz z WebGL Inspector, zobaczysz w śladzie, jeśli wykonasz niepotrzebne instrukcje GL (są oznaczone jasnym żółtym tłem). To może dać ci pojęcie, jak zoptymalizować renderowanie.

Ogólnie rzecz biorąc, sortuj połączenia rysunkowe, aby wszystkie za pomocą tego samego programu, następnie atrybuty, a następnie tekstury, a następnie mundury zostały wykonane w kolejności. W ten sposób będziesz mieć jak najmniej instrukcji GL (i instrukcji JS).

Powiązane problemy