Skip to content

Commit f1760f9

Browse files
committed
Working through splitting the Batch shader out of the manager, and into its own class. Got multi-shader swapping working.
1 parent 33c618d commit f1760f9

5 files changed

Lines changed: 425 additions & 43 deletions

File tree

src/components/Transform.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ Phaser.Component.Transform.prototype = {
176176
this._anchorX = x;
177177
this._anchorY = y;
178178

179-
return this.update();
179+
this.dirty = true;
180180
},
181181

182182
setRotation: function (rotation)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @author Richard Davey <rich@photonstorm.com>
3+
* @copyright 2016 Photon Storm Ltd.
4+
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
5+
*/
6+
7+
/**
8+
* A PixelField is a container, with a position, rotation and scale, that renders pixels.
9+
* So it maintains a list of pixels (just coordinates + a color), and renders them to the batch (maybe with a custom shader?)
10+
*
11+
* @class Phaser.GameObject.PixelField
12+
* @extends Phaser.GameObject
13+
* @constructor
14+
* @param {Phaser.Game} game - A reference to the currently running game.
15+
* @param {number} [x=0] - The x coordinate of the Image. The coordinate is relative to any parent container this Image may be in.
16+
* @param {number} [y=0] - The y coordinate of the Image. The coordinate is relative to any parent container this Image may be in.
17+
* @param {string} [key] - The texture used by the Image during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture.
18+
* @param {string|number} [frame] - If this Image is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
19+
*/
20+
Phaser.GameObject.PixelField = function (game, x, y)
21+
{
22+
this.game = game;
23+
24+
Phaser.GameObject.call(this, game, x, y);
25+
26+
/**
27+
* @property {number} type - The const type of this object.
28+
* @readonly
29+
*/
30+
this.type = Phaser.IMAGE;
31+
};
32+
33+
Phaser.GameObject.PixelField.prototype = Object.create(Phaser.GameObject.prototype);
34+
Phaser.GameObject.PixelField.prototype.constructor = Phaser.GameObject.PixelField;
35+
36+
/**
37+
* Automatically called by World.preUpdate.
38+
*
39+
* @method Phaser.Image#preUpdate
40+
* @memberof Phaser.Image
41+
*/
42+
Phaser.GameObject.PixelField.prototype.preUpdate = function ()
43+
{
44+
if (this.parent)
45+
{
46+
this.color.worldAlpha = this.parent.color.worldAlpha;
47+
}
48+
};
49+
50+
// Phaser.GameObject.Image.prototype.update = function ()
51+
// {
52+
// };
53+
54+
// Phaser.GameObject.Image.prototype.postUpdate = function ()
55+
// {
56+
// };
57+
58+
Object.defineProperties(Phaser.GameObject.PixelField.prototype, {
59+
60+
width: {
61+
62+
enumerable: true,
63+
64+
get: function ()
65+
{
66+
return this.transform._scaleX * this.frame.realWidth;
67+
},
68+
69+
set: function (value)
70+
{
71+
this.scaleX = value / this.frame.realWidth;
72+
}
73+
74+
},
75+
76+
height: {
77+
78+
enumerable: true,
79+
80+
get: function ()
81+
{
82+
return this.transform._scaleY * this.frame.realHeight;
83+
},
84+
85+
set: function (value)
86+
{
87+
this.scaleY = value / this.frame.realHeight;
88+
}
89+
90+
}
91+
92+
});

src/renderer/webgl/BatchManager.js

Lines changed: 115 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Phaser.Renderer.WebGL.BatchManager = function (renderer, batchSize)
3030
// Texture Index (float) = 4 bytes
3131
// Tint Color (float) = 4 bytes
3232
// BG Color (float) = 4 bytes
33+
//
34+
// Total: 28 bytes (per vert) * 4 (4 verts per quad) (= 112 bytes) * maxBatchSize (usually 2000) = 224 kilobytes sent to the GPU every frame
3335

3436
this.vertSize = (4 * 2) + (4 * 2) + (4) + (4) + (4);
3537

@@ -115,7 +117,50 @@ Phaser.Renderer.WebGL.BatchManager = function (renderer, batchSize)
115117
'}'
116118
];
117119

118-
this.multiTextureFragmentSrc = null;
120+
this.fragmentSrc2 = [
121+
'precision lowp float;',
122+
123+
'varying vec2 vTextureCoord;', // the texture coords passed in from the vertex shader
124+
'varying vec4 vTintColor;', // the color value passed in from the vertex shader (texture color + alpha + tint)
125+
'varying vec4 vBgColor;', // the bg color value passed in from the vertex shader
126+
'varying float vTextureIndex;',
127+
128+
'uniform sampler2D uSampler;', // our texture
129+
130+
'void main(void) {',
131+
' gl_FragColor = texture2D(uSampler, vTextureCoord);',
132+
' gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126 * gl_FragColor.r + 0.7152 * gl_FragColor.g + 0.0722 * gl_FragColor.b), 1.0);',
133+
'}'
134+
];
135+
136+
/**
137+
* The multi-texture fragment shader.
138+
* This array is modified heavily by the initMultiTexture method.
139+
* @property multiTextureFragmentSrc
140+
* @type Array
141+
*/
142+
this.multiTextureFragmentSrc = [
143+
'precision lowp float;',
144+
145+
'varying vec2 vTextureCoord;', // the texture coords passed in from the vertex shader
146+
'varying vec4 vTintColor;', // the color value passed in from the vertex shader (texture color + alpha + tint)
147+
'varying vec4 vBgColor;', // the bg color value passed in from the vertex shader
148+
'varying float vTextureIndex;',
149+
150+
'uniform sampler2D uSamplerArray[0];', // this line is replaced when the shader is built, with the actual total
151+
152+
'const vec4 PINK = vec4(1.0, 0.0, 1.0, 1.0);',
153+
154+
'void main(void) {',
155+
' vec4 pixel;',
156+
' if (vTextureIndex == 0.0) pixel = texture2D(uSamplerArray[0], vTextureCoord);',
157+
'// IFELSEBLOCK', // special tag used to insert the multi-texture if else block. Do not edit or remove.
158+
' else pixel = PINK;',
159+
' pixel *= vTintColor;',
160+
// ' if (pixel.a == 0.0) pixel = vBgColor;', // if texture alpha is zero, use the bg color
161+
' gl_FragColor = pixel;',
162+
'}'
163+
];
119164

120165
// @type {GLint}
121166
this.aVertexPosition;
@@ -225,10 +270,14 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
225270
}
226271
},
227272

228-
initAttributes: function ()
273+
initAttributes: function (p)
229274
{
230275
var gl = this.gl;
231-
var program = this.program;
276+
// var program = this.program2;
277+
var program = p;
278+
279+
// Set Shader
280+
gl.useProgram(program);
232281

233282
// Get and store the attributes
234283

@@ -261,8 +310,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
261310

262311
initSingleTexture: function ()
263312
{
264-
console.log('initSingleTexture');
265-
266313
var gl = this.gl;
267314

268315
// Shader already exists
@@ -274,55 +321,53 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
274321
// Compile the Shader
275322
this.program = this.renderer.compileProgram(this.vertexSrc, this.fragmentSrc);
276323

324+
this.program2 = this.renderer.compileProgram(this.vertexSrc, this.fragmentSrc2);
325+
277326
// Set Shader
278-
gl.useProgram(this.program);
327+
// gl.useProgram(this.program);
279328

280-
this.initAttributes();
329+
this.initAttributes(this.program);
281330

282331
this.uSampler = gl.getUniformLocation(this.program, 'uSampler');
283332
},
284333

285334
initMultiTexture: function ()
286335
{
287-
console.log('initMultiTexture');
288-
289336
var gl = this.gl;
290337

291-
var multiFrag = [
292-
'precision lowp float;',
293-
294-
'varying vec2 vTextureCoord;', // the texture coords passed in from the vertex shader
295-
'varying vec4 vTintColor;', // the color value passed in from the vertex shader (texture color + alpha + tint)
296-
'varying vec4 vBgColor;', // the bg color value passed in from the vertex shader
297-
'varying float vTextureIndex;',
298-
299-
'uniform sampler2D uSamplerArray[' + this.renderer.maxTextures + '];',
338+
var block = [];
339+
var splicePoint = 0;
300340

301-
'const vec4 PINK = vec4(1.0, 0.0, 1.0, 1.0);',
302-
303-
'void main(void) {',
304-
305-
' vec4 pixel;',
306-
' if (vTextureIndex == 0.0) pixel = texture2D(uSamplerArray[0], vTextureCoord);'
307-
];
308-
309-
for (var i = 1; i < this.renderer.maxTextures; i++)
341+
// Build the else if block
342+
for (var t = 1; t < this.renderer.maxTextures; t++)
310343
{
311-
multiFrag.push(' else if (vTextureIndex == ' + i + '.0) pixel = texture2D(uSamplerArray[' + i + '], vTextureCoord);');
344+
block.push(' else if (vTextureIndex == ' + t + '.0) pixel = texture2D(uSamplerArray[' + t + '], vTextureCoord);');
312345
}
313346

314-
multiFrag = multiFrag.concat([
315-
' else pixel = PINK;',
347+
// Parse the fragment src array
348+
for (var i = 0; i < this.multiTextureFragmentSrc.length; i++)
349+
{
350+
var line = this.multiTextureFragmentSrc[i];
316351

317-
' pixel *= vTintColor;',
318-
// ' if (pixel.a == 0.0) pixel = vBgColor;', // if texture alpha is zero, use the bg color
319-
' gl_FragColor = pixel;',
320-
'}'
321-
]);
352+
// Inject the maxTextures total into the shader
353+
if (line === 'uniform sampler2D uSamplerArray[0];')
354+
{
355+
this.multiTextureFragmentSrc[i] = 'uniform sampler2D uSamplerArray[' + this.renderer.maxTextures + '];';
356+
}
357+
else if (line === '// IFELSEBLOCK')
358+
{
359+
// Store the index at which we need to insert the if else block
360+
splicePoint = i;
361+
}
362+
}
322363

323-
this.multiTextureFragmentSrc = multiFrag;
364+
// Store the end part of the shader
365+
var shaderEnd = this.multiTextureFragmentSrc.splice(splicePoint);
324366

325-
// Shader already exists
367+
// Stitch it back together again
368+
this.multiTextureFragmentSrc = this.multiTextureFragmentSrc.concat(block, shaderEnd);
369+
370+
// Shader already exists?
326371
if (this.program)
327372
{
328373
this.renderer.deleteProgram(this.program);
@@ -360,6 +405,7 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
360405
this._i = 0;
361406
this.dirty = true;
362407
this.currentBatchSize = 0;
408+
this.initAttributes(this.program);
363409
},
364410

365411
end: function ()
@@ -480,8 +526,8 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
480526
var gl = this.gl;
481527

482528
// Bind the buffers
483-
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
484-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
529+
// gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
530+
// gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
485531

486532
// Set the projection vector. Defaults to the middle of the Game World, on negative y.
487533
// I.e. if the world is 800x600 then the projection vector is 400 x -300
@@ -518,14 +564,17 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
518564

519565
flush: function ()
520566
{
567+
var gl = this.gl;
568+
521569
// Always dirty the first pass through but subsequent calls may be clean
522570
if (this.dirty)
523571
{
572+
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
573+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
574+
524575
this.initShader();
525576
}
526577

527-
var gl = this.gl;
528-
529578
// Upload the vertex data to the GPU - is this cheaper (overall) than creating a new TypedArray view?
530579
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
531580

@@ -569,6 +618,31 @@ Phaser.Renderer.WebGL.BatchManager.prototype = {
569618
this.renderer.setBlendMode(sprite.blendMode);
570619
}
571620

621+
if (sprite.shader === 2)
622+
{
623+
gl.drawElements(gl.TRIANGLES, currentSize * 6, gl.UNSIGNED_SHORT, start * 6 * 2);
624+
this.renderer.drawCount++;
625+
626+
// Reset the batch
627+
start = i;
628+
currentSize = 0;
629+
630+
this.initAttributes(this.program2);
631+
this.initShader();
632+
}
633+
else if (sprite.shader === 1)
634+
{
635+
gl.drawElements(gl.TRIANGLES, currentSize * 6, gl.UNSIGNED_SHORT, start * 6 * 2);
636+
this.renderer.drawCount++;
637+
638+
// Reset the batch
639+
start = i;
640+
currentSize = 0;
641+
642+
this.initAttributes(this.program);
643+
this.initShader();
644+
}
645+
572646
// TODO: Check for shader here
573647

574648
// If either blend or shader set, we need to drawElements and swap

src/renderer/webgl/WebGLRenderer.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Phaser.Renderer.WebGL = function (game)
2121

2222
this.type = Phaser.WEBGL;
2323

24-
this.shaderID = 0;
24+
this.currentShader = null;
2525

2626
/**
2727
* This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
@@ -318,6 +318,7 @@ Phaser.Renderer.WebGL.prototype = {
318318
this.projection.y = -(this.height / 2) / this.game.resolution;
319319
},
320320

321+
/*
321322
getShaderID: function (shader)
322323
{
323324
this.shaderID++;
@@ -326,6 +327,7 @@ Phaser.Renderer.WebGL.prototype = {
326327
327328
return this.shaderID;
328329
},
330+
*/
329331

330332
/**
331333
* If Multi Texture support has been enabled, then calling this method will enable batching on the given

0 commit comments

Comments
 (0)