Skip to content

Commit 3c248d1

Browse files
committed
Added in the FX Batch processor.
1 parent 5ec168a commit 3c248d1

1 file changed

Lines changed: 357 additions & 0 deletions

File tree

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
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+
* Standard Image and Sprite Shader.
9+
*
10+
* @class Phaser.Renderer.WebGL.Batch.Image
11+
* @constructor
12+
* @param {Phaser.Renderer.WebGL} renderer - The WebGL Renderer.
13+
*/
14+
Phaser.Renderer.WebGL.Batch.FX = function (manager, batchSize)
15+
{
16+
// Vertex Data Size is calculated by adding together:
17+
//
18+
// Position (vec2) = 4 * 2 = 8 bytes
19+
// UV (vec2) = 4 * 2 = 8 bytes
20+
// Texture Index (float) = 4 bytes
21+
// Tint Color (float) = 4 bytes
22+
// BG Color (float) = 4 bytes
23+
//
24+
// Total: 28 bytes (per vert) * 4 (4 verts per quad) (= 112 bytes) * maxSize (usually 2000) = 224 kilobytes sent to the GPU every frame
25+
26+
var vertSize = (4 * 2) + (4 * 2) + (4) + (4) + (4);
27+
28+
Phaser.Renderer.WebGL.Batch.call(this, manager, batchSize, vertSize);
29+
30+
// View on the vertices as a Float32Array
31+
this.positions = new Float32Array(this.vertices);
32+
33+
// View on the vertices as a Uint32Array
34+
this.colors = new Uint32Array(this.vertices);
35+
36+
// Attributes and Uniforms specific to this Batch Shader
37+
38+
// @type {GLint}
39+
this.aVertexPosition;
40+
41+
// @type {GLint}
42+
this.aTextureCoord;
43+
44+
// @type {GLint}
45+
this.aTextureIndex;
46+
47+
// @type {GLint}
48+
this.aTintColor;
49+
50+
// @type {GLint}
51+
this.aBgColor;
52+
53+
// @type {WebGLUniformLocation}
54+
this.uSampler;
55+
56+
// @type {WebGLUniformLocation}
57+
this.projectionVector;
58+
59+
// @type {WebGLUniformLocation}
60+
this.offsetVector;
61+
};
62+
63+
Phaser.Renderer.WebGL.Batch.FX.prototype = Object.create(Phaser.Renderer.WebGL.Batch.prototype);
64+
65+
Phaser.Renderer.WebGL.Batch.FX.prototype.constructor = Phaser.Renderer.WebGL.Batch.FX;
66+
67+
Phaser.Renderer.WebGL.Batch.FX.prototype.init = function ()
68+
{
69+
this.gl = this.renderer.gl;
70+
71+
this.vertexSrc = [
72+
'attribute vec2 aVertexPosition;',
73+
'attribute vec2 aTextureCoord;',
74+
'attribute float aTextureIndex;',
75+
'attribute vec4 aTintColor;',
76+
'attribute vec4 aBgColor;',
77+
78+
'uniform vec2 projectionVector;',
79+
'uniform vec2 offsetVector;',
80+
81+
'varying vec2 vTextureCoord;',
82+
'varying vec4 vTintColor;',
83+
'varying vec4 vBgColor;',
84+
'varying float vTextureIndex;',
85+
86+
'const vec2 center = vec2(-1.0, 1.0);',
87+
88+
'void main(void) {',
89+
' if (aTextureIndex > 0.0) gl_Position = vec4(0.0);',
90+
' gl_Position = vec4(((aVertexPosition + offsetVector) / projectionVector) + center, 0.0, 1.0);',
91+
' vTextureCoord = aTextureCoord;', // pass the texture coordinate to the fragment shader, the GPU will interpolate the points
92+
' vTintColor = vec4(aTintColor.rgb * aTintColor.a, aTintColor.a);',
93+
' vBgColor = aBgColor;',
94+
' vTextureIndex = aTextureIndex;',
95+
'}'
96+
];
97+
98+
this.fragmentSrc = [
99+
'precision lowp float;',
100+
101+
'varying vec2 vTextureCoord;', // the texture coords passed in from the vertex shader
102+
'varying vec4 vTintColor;', // the color value passed in from the vertex shader (texture color + alpha + tint)
103+
'varying vec4 vBgColor;', // the bg color value passed in from the vertex shader
104+
'varying float vTextureIndex;',
105+
106+
'uniform sampler2D uSampler;', // our texture
107+
108+
'void main(void) {',
109+
' gl_FragColor = texture2D(uSampler, vTextureCoord);',
110+
' 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);',
111+
'}'
112+
];
113+
114+
// Compile the Shader
115+
this.program = this.renderer.compileProgram(this.vertexSrc, this.fragmentSrc);
116+
117+
// Our static index buffer, calculated once at the start of our game
118+
119+
// This contains the indices data for the quads.
120+
//
121+
// A quad is made up of 2 triangles (A and B in the image below)
122+
//
123+
// 0 = Top Left
124+
// 1 = Top Right
125+
// 2 = Bottom Right
126+
// 3 = Bottom Left
127+
//
128+
// 0----1
129+
// |\ A|
130+
// | \ |
131+
// | \ |
132+
// | B \|
133+
// | \
134+
// 3----2
135+
//
136+
// Because triangles A and B share 2 points (0 and 2) the vertex buffer only stores
137+
// 4 sets of data (top-left, top-right, bottom-left and bottom-right), which is why
138+
// the indices offsets uses the j += 4 iteration. Indices array has to contain 3
139+
// entries for every triangle (so 6 for every quad), but our vertex data compacts
140+
// that down, as we don't want to fill it with loads of DUPLICATE data, so the
141+
// indices array is a look-up table, telling WebGL where in the vertex buffer to look
142+
// for that triangles indice data.
143+
144+
// batchSize * vertSize = 2000 * 6 (because we store 6 pieces of vertex data per triangle)
145+
// and up to a maximum of 2000 entries in the batch
146+
147+
for (var i = 0, j = 0; i < (this.maxSize * this.vertSize); i += 6, j += 4)
148+
{
149+
// Triangle 1
150+
this.indices[i + 0] = j + 0; // Top Left
151+
this.indices[i + 1] = j + 1; // Top Right
152+
this.indices[i + 2] = j + 2; // Bottom Right
153+
154+
// Triangle 2
155+
this.indices[i + 3] = j + 0; // Top Left
156+
this.indices[i + 4] = j + 2; // Bottom Right
157+
this.indices[i + 5] = j + 3; // Bottom Left
158+
}
159+
160+
var gl = this.gl;
161+
162+
// Create indices buffer
163+
this.indexBuffer = gl.createBuffer();
164+
165+
// Bind it
166+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
167+
168+
// Set the source of the buffer data (this.indices array)
169+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
170+
171+
// Create Vertex Data buffer
172+
this.vertexBuffer = gl.createBuffer();
173+
174+
// Bind it
175+
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
176+
177+
// Set the source of the buffer data (this.vertices array)
178+
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
179+
180+
};
181+
182+
Phaser.Renderer.WebGL.Batch.FX.prototype.bindShader = function ()
183+
{
184+
var gl = this.gl;
185+
var program = this.program;
186+
var vertSize = this.vertSize;
187+
188+
// Set Shader
189+
gl.useProgram(program);
190+
191+
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
192+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
193+
194+
// Get and store the attributes
195+
196+
// vertex position
197+
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
198+
gl.enableVertexAttribArray(this.aVertexPosition);
199+
200+
// texture coordinate
201+
this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
202+
gl.enableVertexAttribArray(this.aTextureCoord);
203+
204+
// texture index
205+
this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex');
206+
gl.enableVertexAttribArray(this.aTextureIndex);
207+
208+
// tint / pixel color
209+
this.aTintColor = gl.getAttribLocation(program, 'aTintColor');
210+
gl.enableVertexAttribArray(this.aTintColor);
211+
212+
// background pixel color
213+
this.aBgColor = gl.getAttribLocation(program, 'aBgColor');
214+
gl.enableVertexAttribArray(this.aBgColor);
215+
216+
// The projection vector (middle of the game world)
217+
this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
218+
219+
// The offset vector (camera shake)
220+
this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
221+
222+
// The Texture Sampler
223+
this.uSampler = gl.getUniformLocation(this.program, 'uSampler');
224+
225+
// Set the projection vector. Defaults to the middle of the Game World, on negative y.
226+
// I.e. if the world is 800x600 then the projection vector is 400 x -300
227+
gl.uniform2f(this.projectionVector, this.renderer.projection.x, this.renderer.projection.y);
228+
229+
// Set the offset vector.
230+
gl.uniform2f(this.offsetVector, this.renderer.offset.x, this.renderer.offset.y);
231+
232+
// The Vertex Position (x/y)
233+
// 2 FLOATS, 2 * 4 = 8 bytes. Index pos: 0 to 7
234+
// final argument = the offset within the vertex input
235+
gl.vertexAttribPointer(this.aVertexPosition, 2, gl.FLOAT, false, vertSize, 0);
236+
237+
// The Texture Coordinate (uvx/uvy)
238+
// 2 FLOATS, 2 * 4 = 8 bytes. Index pos: 8 to 15
239+
gl.vertexAttribPointer(this.aTextureCoord, 2, gl.FLOAT, false, vertSize, 8);
240+
241+
// Texture Index
242+
// 1 FLOAT, 4 bytes. Index pos: 16 to 19
243+
gl.vertexAttribPointer(this.aTextureIndex, 1, gl.FLOAT, false, vertSize, 16);
244+
245+
// Tint color
246+
// 4 UNSIGNED BYTES, 4 bytes. Index pos: 20 to 23
247+
// Attributes will be interpreted as unsigned bytes and normalized
248+
gl.vertexAttribPointer(this.aTintColor, 4, gl.UNSIGNED_BYTE, true, vertSize, 20);
249+
250+
// Background Color
251+
// 4 UNSIGNED BYTES, 4 bytes. Index pos: 24 to 27
252+
// Attributes will be interpreted as unsigned bytes and normalized
253+
gl.vertexAttribPointer(this.aBgColor, 4, gl.UNSIGNED_BYTE, true, vertSize, 24);
254+
};
255+
256+
Phaser.Renderer.WebGL.Batch.FX.prototype.add = function (verts, uvs, textureIndex, alpha, tintColors, bgColors)
257+
{
258+
// These are TypedArray Views into the vertices ArrayBuffer
259+
var colors = this.colors;
260+
var positions = this.positions;
261+
262+
var i = this._i;
263+
264+
// Top Left vert (xy, uv, color)
265+
positions[i++] = verts.x0;
266+
positions[i++] = verts.y0;
267+
positions[i++] = uvs.x0;
268+
positions[i++] = uvs.y0;
269+
positions[i++] = textureIndex;
270+
colors[i++] = tintColors.topLeft + alpha;
271+
colors[i++] = bgColors.topLeft;
272+
273+
// Top Right vert (xy, uv, color)
274+
positions[i++] = verts.x1;
275+
positions[i++] = verts.y1;
276+
positions[i++] = uvs.x1;
277+
positions[i++] = uvs.y1;
278+
positions[i++] = textureIndex;
279+
colors[i++] = tintColors.topRight + alpha;
280+
colors[i++] = bgColors.topRight;
281+
282+
// Bottom Right vert (xy, uv, color)
283+
positions[i++] = verts.x2;
284+
positions[i++] = verts.y2;
285+
positions[i++] = uvs.x2;
286+
positions[i++] = uvs.y2;
287+
positions[i++] = textureIndex;
288+
colors[i++] = tintColors.bottomRight + alpha;
289+
colors[i++] = bgColors.bottomRight;
290+
291+
// Bottom Left vert (xy, uv, color)
292+
positions[i++] = verts.x3;
293+
positions[i++] = verts.y3;
294+
positions[i++] = uvs.x3;
295+
positions[i++] = uvs.y3;
296+
positions[i++] = textureIndex;
297+
colors[i++] = tintColors.bottomLeft + alpha;
298+
colors[i++] = bgColors.bottomLeft;
299+
300+
this._i = i;
301+
302+
this.size++;
303+
};
304+
305+
Phaser.Renderer.WebGL.Batch.FX.prototype.flush = function ()
306+
{
307+
if (this.size === 0)
308+
{
309+
return;
310+
}
311+
312+
var gl = this.gl;
313+
314+
// Upload the vertex data to the GPU - is this cheaper (overall) than creating a new TypedArray view?
315+
// The tradeoff is sending 224KB of data to the GPU every frame, even if most of it is empty should the
316+
// batch be only slightly populated, vs. the creation of a new TypedArray view and its corresponding gc every frame.
317+
318+
if (this.size > this.halfSize)
319+
{
320+
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
321+
}
322+
else
323+
{
324+
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
325+
326+
this.view = this.positions.subarray(0, this.size * this.vertSize);
327+
328+
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.view);
329+
}
330+
331+
gl.drawElements(gl.TRIANGLES, this.size * 6, gl.UNSIGNED_SHORT, 0);
332+
333+
this.renderer.drawCount++;
334+
335+
// Reset the batch
336+
this.size = 0;
337+
338+
this._i = 0;
339+
};
340+
341+
Phaser.Renderer.WebGL.Batch.FX.prototype.destroy = function ()
342+
{
343+
this.vertices = null;
344+
this.indices = null;
345+
this.view = null;
346+
347+
this.gl.deleteBuffer(this.vertexBuffer);
348+
this.gl.deleteBuffer(this.indexBuffer);
349+
350+
this.renderer.deleteProgram(this.program);
351+
352+
this.renderer = null;
353+
354+
this.gl = null;
355+
356+
this.manager = null;
357+
};

0 commit comments

Comments
 (0)