Blend Modes work. More optimisations in the batch manager.

This commit is contained in:
Richard Davey 2016-10-25 03:57:34 +01:00
parent dae0b2c124
commit 120b6eb2bd
5 changed files with 74 additions and 131 deletions

View file

@ -71,13 +71,6 @@ Phaser.Renderer.WebGL.GameObjects.Image = {
} }
*/ */
// Also works ...
// renderer.spriteBatch.startGameObject(src);
// renderer.spriteBatch.addVert(verts.x0, verts.y0, uvs.x0, uvs.y0, index, tint.topLeft + alpha, bg);
// renderer.spriteBatch.addVert(verts.x1, verts.y1, uvs.x1, uvs.y1, index, tint.topRight + alpha, bg);
// renderer.spriteBatch.addVert(verts.x2, verts.y2, uvs.x2, uvs.y2, index, tint.bottomRight + alpha, bg);
// renderer.spriteBatch.addVert(verts.x3, verts.y3, uvs.x3, uvs.y3, index, tint.bottomLeft + alpha, bg);
} }
}; };

View file

@ -47,10 +47,10 @@ Phaser.Renderer.WebGL.BatchManager = function (renderer, batchSize)
// View on the vertices as a Uint32Array // View on the vertices as a Uint32Array
this.colors = new Uint32Array(this.vertices); this.colors = new Uint32Array(this.vertices);
this.currentTextureSource = null;
this.currentBatchSize = 0; this.currentBatchSize = 0;
this.dirty = true; this.dirty = true;
this.list = []; this.list = [];
/** /**
@ -109,8 +109,9 @@ Phaser.Renderer.WebGL.BatchManager = function (renderer, batchSize)
'uniform sampler2D uSampler;', // our texture 'uniform sampler2D uSampler;', // our texture
'void main(void) {', 'void main(void) {',
' vec4 pixel = texture2D(uSampler, vTextureCoord) * vTintColor;', // get the color from the texture ' vec4 pixel = texture2D(uSampler, vTextureCoord);', // get the color from the texture
' if (pixel.a == 0.0) pixel = vBgColor;', // if texture alpha is zero, use the bg color // ' vec4 pixel = texture2D(uSampler, vTextureCoord) * vTintColor;', // get the color from the texture
// ' if (pixel.a == 0.0) pixel = vBgColor;', // if texture alpha is zero, use the bg color
' gl_FragColor = pixel;', ' gl_FragColor = pixel;',
'}' '}'
]; ];
@ -217,7 +218,7 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
var fragmentSrc = this.fragmentSrc; var fragmentSrc = this.fragmentSrc;
if (this.renderer.multiTexture && this.renderer.maxTextures > 1) if (this.renderer.multiTexture)
{ {
var multiFrag = [ var multiFrag = [
'precision lowp float;', 'precision lowp float;',
@ -243,22 +244,20 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
} }
multiFrag = multiFrag.concat([ multiFrag = multiFrag.concat([
// ' else pixel = PINK;', ' else pixel = PINK;',
' pixel *= vTintColor;', ' pixel *= vTintColor;',
// ' if (pixel.a == 0.0) pixel = vBgColor;', // if texture alpha is zero, use the bg color ' if (pixel.a == 0.0) pixel = vBgColor;', // if texture alpha is zero, use the bg color
' gl_FragColor = pixel;', ' gl_FragColor = pixel;',
// ' gl_FragColor = PINK;',
'}' '}'
]); ]);
this.multiTextureFragmentSrc = multiFrag; this.multiTextureFragmentSrc = multiFrag;
fragmentSrc = this.multiTextureFragmentSrc; fragmentSrc = this.multiTextureFragmentSrc;
// console.dir(this.multiTextureFragmentSrc);
} }
// Compile the Shaders // Compile the Shader
var program = this.renderer.compileProgram(this.vertexSrc, fragmentSrc); var program = this.renderer.compileProgram(this.vertexSrc, fragmentSrc);
// Set Shader // Set Shader
@ -283,7 +282,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
gl.enableVertexAttribArray(this.aBgColor); gl.enableVertexAttribArray(this.aBgColor);
// Get and store the uniforms for the shader // Get and store the uniforms for the shader
// this part is different for multi-textures
if (this.renderer.multiTexture) if (this.renderer.multiTexture)
{ {
// Bind empty multi-textures to avoid WebGL spam // Bind empty multi-textures to avoid WebGL spam
@ -302,8 +300,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
this.uSampler = gl.getUniformLocation(program, 'uSamplerArray[0]'); this.uSampler = gl.getUniformLocation(program, 'uSamplerArray[0]');
gl.activeTexture(gl.TEXTURE0);
gl.uniform1iv(this.uSampler, indices); gl.uniform1iv(this.uSampler, indices);
} }
else else
@ -314,7 +310,7 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
// The projection vector (middle of the game world) // The projection vector (middle of the game world)
this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
// The offset vector // The offset vector (camera shake)
this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
this.program = program; this.program = program;
@ -324,7 +320,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
{ {
this._i = 0; this._i = 0;
this.dirty = true; this.dirty = true;
this.currentTextureSource = null;
this.currentBatchSize = 0; this.currentBatchSize = 0;
}, },
@ -354,92 +349,23 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
setCurrentTexture: function (textureSource) setCurrentTexture: function (textureSource)
{ {
var gl = this.gl; if (this.renderer.textureArray[textureSource.glTextureIndex] !== textureSource)
if (this.renderer.multiTexture)
{ {
if (this.renderer.textureArray[textureSource.glTextureIndex] !== textureSource)
{
// console.log('setCurrentTexture - batch size', this.currentBatchSize);
// console.log(textureSource);
if (this.currentBatchSize > 0)
{
this.flush();
}
gl.activeTexture(gl.TEXTURE0 + textureSource.glTextureIndex);
// console.log('activeTexture', gl.TEXTURE0 + textureSource.glTextureIndex);
gl.bindTexture(gl.TEXTURE_2D, textureSource.glTexture);
this.renderer.textureArray[textureSource.glTextureIndex] = textureSource;
}
}
else
{
// console.log('setCurrentTexture');
if (this.currentTextureSource === textureSource)
{
// console.log('skip');
return;
}
if (this.currentBatchSize > 0) if (this.currentBatchSize > 0)
{ {
// console.log('force flush from insdie setCurrentTexture');
this.flush(); this.flush();
} }
// console.log('bind new sprites texture', textureSource.image.currentSrc); var gl = this.gl;
gl.activeTexture(gl.TEXTURE0); gl.activeTexture(gl.TEXTURE0 + textureSource.glTextureIndex);
gl.bindTexture(gl.TEXTURE_2D, textureSource.glTexture); gl.bindTexture(gl.TEXTURE_2D, textureSource.glTexture);
this.currentTextureSource = textureSource; this.renderer.textureArray[textureSource.glTextureIndex] = textureSource;
} }
}, },
// Call 'addVert' x4 after calling this
startGameObject: function (gameObject)
{
// Does this Game Objects texture need updating?
if (gameObject.frame.source.glDirty)
{
this.renderer.updateTexture(gameObject.frame.source);
}
// Check Batch Size
if (this.currentBatchSize >= this.maxBatchSize)
{
this.flush();
}
this.list[this.currentBatchSize++] = gameObject;
},
// Call this 4 times, once for each vert
// Then call addGameObject to complete it (or before it, doesn't matter which order)
addVert: function (x, y, uvx, uvy, textureIndex, tint, bg)
{
var i = this._i;
this.positions[i++] = x;
this.positions[i++] = y;
this.positions[i++] = uvx;
this.positions[i++] = uvy;
this.positions[i++] = textureIndex;
this.colors[i++] = tint;
this.colors[i++] = bg;
this._i = i;
},
addToBatch: function (gameObject, verts, uvs, textureIndex, alpha, tintColors, bgColors) addToBatch: function (gameObject, verts, uvs, textureIndex, alpha, tintColors, bgColors)
{ {
// console.log('addToBatch', gameObject.frame.name); // console.log('addToBatch', gameObject.frame.name);
@ -447,26 +373,39 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
// Check Batch Size and flush if needed // Check Batch Size and flush if needed
if (this.currentBatchSize >= this.maxBatchSize) if (this.currentBatchSize >= this.maxBatchSize)
{ {
// console.log('force flush because batch limit hit');
this.flush(); this.flush();
} }
var source = gameObject.frame.source;
// Does this Game Objects texture need updating? // Does this Game Objects texture need updating?
if (gameObject.frame.source.glDirty) if (source.glDirty)
{ {
// Check Batch Size and flush if needed // Check Batch Size and flush if needed
if (this.currentBatchSize > 0) if (this.currentBatchSize > 0)
{ {
// console.log('force flush before updateTexture');
this.flush(); this.flush();
} }
// console.log('texture dirty'); this.renderer.updateTexture(source);
this.renderer.updateTexture(gameObject.frame.source);
} }
// Set texture // Set the current texture (not if this sprite has its own shader?)
this.setCurrentTexture(gameObject.frame.source); if (this.renderer.textureArray[source.glTextureIndex] !== source)
{
var gl = this.gl;
if (this.currentBatchSize > 0)
{
this.flush();
}
gl.activeTexture(gl.TEXTURE0 + source.glTextureIndex);
gl.bindTexture(gl.TEXTURE_2D, source.glTexture);
this.renderer.textureArray[source.glTextureIndex] = source;
}
// These are TypedArray Views into the vertices ArrayBuffer // These are TypedArray Views into the vertices ArrayBuffer
var colors = this.colors; var colors = this.colors;
@ -474,8 +413,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
var i = this._i; var i = this._i;
// console.log('addToBatch i / ci', i, this.currentBatchSize);
// Top Left vert (xy, uv, color) // Top Left vert (xy, uv, color)
positions[i++] = verts.x0; positions[i++] = verts.x0;
positions[i++] = verts.y0; positions[i++] = verts.y0;
@ -521,9 +458,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
{ {
var gl = this.gl; var gl = this.gl;
// Bind the main texture
// gl.activeTexture(gl.TEXTURE0);
// Bind the buffers // Bind the buffers
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
@ -563,8 +497,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
flush: function () flush: function ()
{ {
// console.log('flush');
// Always dirty the first pass through but subsequent calls may be clean // Always dirty the first pass through but subsequent calls may be clean
if (this.dirty) if (this.dirty)
{ {
@ -573,7 +505,10 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
var gl = this.gl; var gl = this.gl;
// Upload verts to the buffer // Upload the vertex data to the GPU - is this cheaper (overall) than creating a new TypedArray view?
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
/*
if (this.currentBatchSize > this.halfBatchSize) if (this.currentBatchSize > this.halfBatchSize)
{ {
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
@ -587,6 +522,7 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
} }
*/
var sprite; var sprite;
@ -599,15 +535,24 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
if (sprite.blendMode !== this.renderer.currentBlendMode) if (sprite.blendMode !== this.renderer.currentBlendMode)
{ {
if (currentSize > 0)
{
gl.drawElements(gl.TRIANGLES, currentSize * 6, gl.UNSIGNED_SHORT, start * 6 * 2);
this.renderer.drawCount++;
}
this.renderer.setBlendMode(sprite.blendMode); this.renderer.setBlendMode(sprite.blendMode);
} }
// TODO: Check for shader here
// If either blend or shader set, we need to drawElements and swap
currentSize++; currentSize++;
} }
if (currentSize > 0) if (currentSize > 0)
{ {
// console.log('flushed and drew', currentSize);
gl.drawElements(gl.TRIANGLES, currentSize * 6, gl.UNSIGNED_SHORT, start * 6 * 2); gl.drawElements(gl.TRIANGLES, currentSize * 6, gl.UNSIGNED_SHORT, start * 6 * 2);
this.renderer.drawCount++; this.renderer.drawCount++;
} }
@ -625,8 +570,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
this.gl.deleteBuffer(this.vertexBuffer); this.gl.deleteBuffer(this.vertexBuffer);
this.gl.deleteBuffer(this.indexBuffer); this.gl.deleteBuffer(this.indexBuffer);
this.currentTextureSource = null;
this.renderer = null; this.renderer = null;
this.gl = null; this.gl = null;
} }

View file

@ -86,7 +86,7 @@ Phaser.Renderer.WebGL = function (game)
*/ */
this.stencilBufferLimit = 6; this.stencilBufferLimit = 6;
this.multiTexture = true; this.multiTexture = false;
this.extensions = {}; this.extensions = {};
@ -111,6 +111,14 @@ Phaser.Renderer.WebGL = function (game)
preserveDrawingBuffer: this.preserveDrawingBuffer preserveDrawingBuffer: this.preserveDrawingBuffer
}; };
// this.contextOptions = {
// alpha: true,
// antialias: true,
// premultipliedAlpha: true,
// stencil: true,
// preserveDrawingBuffer: false
// };
/** /**
* @property projection * @property projection
* @type Point * @type Point
@ -155,7 +163,7 @@ Phaser.Renderer.WebGL = function (game)
this.textureArray = []; this.textureArray = [];
this.currentBlendMode = 0; this.currentBlendMode = -1;
this.currentTextureSource = null; this.currentTextureSource = null;
this.blendModes = []; this.blendModes = [];
@ -399,14 +407,15 @@ Phaser.Renderer.WebGL.prototype = {
// Transparent // Transparent
// gl.clearColor(0, 0, 0, 0); // gl.clearColor(0, 0, 0, 0);
// gl.clearColor(0.0, 0.4, 0.0, 1.0); // If 'alpha' is true in the context options then
// gl.clear(gl.COLOR_BUFFER_BIT); // things like blend mode ADD looks really weird
// if you don't clear the background (would look fine over a background image though)
// Black // Black
// gl.clearColor(0, 0, 0, 1); // gl.clearColor(0, 0, 0, 1);
// gl.clear(gl.COLOR_BUFFER_BIT); // gl.clear(gl.COLOR_BUFFER_BIT);
this.setBlendMode(this.blendModes.NORMAL); this.setBlendMode(Phaser.blendModes.NORMAL);
/* /*
if (this.clearBeforeRender) if (this.clearBeforeRender)
@ -441,7 +450,7 @@ Phaser.Renderer.WebGL.prototype = {
this.spriteBatch.begin(); this.spriteBatch.begin();
this.filterManager.begin(); // this.filterManager.begin();
// console.log('render stage'); // console.log('render stage');
@ -478,8 +487,6 @@ Phaser.Renderer.WebGL.prototype = {
// Takes a TextureSource object // Takes a TextureSource object
updateTexture: function (source) updateTexture: function (source)
{ {
console.log('updateTexture', source.image.currentSrc);
if (source.compressionAlgorithm) if (source.compressionAlgorithm)
{ {
return this.updateCompressedTexture(source); return this.updateCompressedTexture(source);
@ -498,6 +505,8 @@ Phaser.Renderer.WebGL.prototype = {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, source.premultipliedAlpha); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, source.premultipliedAlpha);
// Throws a warning in Firefox: WebGL: texImage2D: Chosen format/type incured an expensive reformat: 0x1908/0x1401
// @see https://github.com/mrdoob/three.js/issues/9109
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source.image); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, source.scaleMode === Phaser.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, source.scaleMode === Phaser.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
@ -587,18 +596,18 @@ Phaser.Renderer.WebGL.prototype = {
// Blend Mode Manager // Blend Mode Manager
setBlendMode: function (blendMode) setBlendMode: function (newBlendMode)
{ {
if (this.currentBlendMode === blendMode) if (this.currentBlendMode === newBlendMode)
{ {
return false; return false;
} }
var blendModeWebGL = this.blendModes[this.currentBlendMode]; var blendModeWebGL = this.blendModes[newBlendMode];
if (blendModeWebGL) if (blendModeWebGL)
{ {
this.currentBlendMode = blendMode; this.currentBlendMode = newBlendMode;
this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]);

View file

@ -94,8 +94,6 @@ Phaser.Texture.prototype = {
{ {
this.source[i].glTextureIndex = index; this.source[i].glTextureIndex = index;
console.log('setTextureIndex', index);
index++; index++;
} }

View file

@ -108,10 +108,10 @@ Phaser.RequestAnimationFrame.prototype = {
if (this.isRunning) if (this.isRunning)
{ {
this._timeOutID = window.requestAnimationFrame(this._onLoop);
// floor the rafTime to make it equivalent to the Date.now() provided by updateSetTimeout (just below) // floor the rafTime to make it equivalent to the Date.now() provided by updateSetTimeout (just below)
this.game.update(Math.floor(rafTime)); this.game.update(Math.floor(rafTime));
this._timeOutID = window.requestAnimationFrame(this._onLoop);
} }
}, },