Skip to content

Commit 4beffe8

Browse files
committed
Texture batching during the batch flush has been implemented in the TextureTintPipeline which resolves the issues of very low frame rates, especially on iOS devices, when using non-batched textures such as those used by Text or TileSprites.
1 parent 8faafc2 commit 4beffe8

2 files changed

Lines changed: 150 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
* `TileSprite.setFrame` has had both the `updateSize` and `updateOrigin` arguments removed as they didn't do anything for TileSprites and were misleading.
1616
* `CameraManager.remove` has a new argument `runDestroy` which, if set, will automatically call `Camera.destroy` on the Cameras removed from the Camera Manager. You should nearly always allow this to happen (thanks jamespierce)
1717
* Device.OS has been restructured to allow fake UAs from Chrome dev tools to register iOS devices.
18+
* Texture batching during the batch flush has been implemented in the TextureTintPipeline which resolves the issues of very low frame rates, especially on iOS devices, when using non-batched textures such as those used by Text or TileSprites. Fix #4110 #4086 (thanks @ivan @sachinhosmani @maximtsai @alexeymolchan)
1819

1920
### Bug Fixes
2021

2122
* Fixed a bug in the canvas rendering of both the Static and Dynamic Tilemap Layers where the camera matrix was being multiplied twice with the layer, causing the scale and placement to be off (thanks galerijanamar)
2223
* If you set `pixelArt` to true in your game config (or `antialias` to false) then TileSprites will now respect this when using the Canvas Renderer and disable smoothing on the internal fill canvas.
2324
* TileSprites that were set to be interactive before they had rendered once wouldn't receive a valid input hit area, causing input to fail. They now define their size immediately, allowing them to be made interactive without having rendered. Fix #4085 (thanks @DotTheGreat)
2425
* The Particle Emitter Manager has been given a NOOP method called `setBlendMode` to stop warnings from being thrown if you added an emitter to a Container in the Canvas renderer. Fix #4083 (thanks @maximtsai)
26+
*
2527

2628
### Examples and TypeScript
2729

src/renderer/webgl/pipelines/TextureTintPipeline.js

Lines changed: 148 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ var TextureTintPipeline = new Class({
121121
*/
122122
this.maxQuads = rendererConfig.batchSize;
123123

124+
/**
125+
* Collection of batch information
126+
*
127+
* @name Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline#batches
128+
* @type {array}
129+
* @since 3.1.0
130+
*/
131+
this.batches = [];
132+
124133
/**
125134
* A temporary Transform Matrix, re-used internally during batching.
126135
*
@@ -269,6 +278,11 @@ var TextureTintPipeline = new Class({
269278

270279
this.mvpUpdate();
271280

281+
if (this.batches.length === 0)
282+
{
283+
this.pushBatch();
284+
}
285+
272286
return this;
273287
},
274288

@@ -307,11 +321,65 @@ var TextureTintPipeline = new Class({
307321
*/
308322
setTexture2D: function (texture, unit)
309323
{
310-
this.renderer.setTexture2D(texture, unit);
324+
if (!texture)
325+
{
326+
texture = this.renderer.blankTexture.glTexture;
327+
unit = 0;
328+
}
329+
330+
var batches = this.batches;
331+
332+
if (batches.length === 0)
333+
{
334+
this.pushBatch();
335+
}
336+
337+
var batch = batches[batches.length - 1];
338+
339+
if (unit > 0)
340+
{
341+
if (batch.textures[unit - 1] &&
342+
batch.textures[unit - 1] !== texture)
343+
{
344+
this.pushBatch();
345+
}
346+
347+
batches[batches.length - 1].textures[unit - 1] = texture;
348+
}
349+
else
350+
{
351+
if (batch.texture !== null &&
352+
batch.texture !== texture)
353+
{
354+
this.pushBatch();
355+
}
356+
357+
batches[batches.length - 1].texture = texture;
358+
}
311359

312360
return this;
313361
},
314362

363+
/**
364+
* Creates a new batch object and pushes it to a batch array.
365+
* The batch object contains information relevant to the current
366+
* vertex batch like the offset in the vertex buffer, vertex count and
367+
* the textures used by that batch.
368+
*
369+
* @method Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline#pushBatch
370+
* @since 3.1.0
371+
*/
372+
pushBatch: function ()
373+
{
374+
var batch = {
375+
first: this.vertexCount,
376+
texture: null,
377+
textures: []
378+
};
379+
380+
this.batches.push(batch);
381+
},
382+
315383
/**
316384
* Uploads the vertex data and emits a draw call for the current batch of vertices.
317385
*
@@ -322,7 +390,10 @@ var TextureTintPipeline = new Class({
322390
*/
323391
flush: function ()
324392
{
325-
if (this.flushLocked) { return this; }
393+
if (this.flushLocked)
394+
{
395+
return this;
396+
}
326397

327398
this.flushLocked = true;
328399

@@ -332,18 +403,88 @@ var TextureTintPipeline = new Class({
332403
var vertexSize = this.vertexSize;
333404
var renderer = this.renderer;
334405

335-
if (vertexCount === 0)
406+
var batches = this.batches;
407+
var batchCount = batches.length;
408+
var batchVertexCount = 0;
409+
var batch = null;
410+
var batchNext;
411+
var textureIndex;
412+
var nTexture;
413+
414+
if (batchCount === 0 || vertexCount === 0)
336415
{
337416
this.flushLocked = false;
338-
return;
339-
}
340417

341-
renderer.setBlankTexture();
418+
return this;
419+
}
342420

343421
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.bytes.subarray(0, vertexCount * vertexSize));
344-
gl.drawArrays(topology, 0, vertexCount);
422+
423+
for (var index = 0; index < batches.length - 1; index++)
424+
{
425+
batch = batches[index];
426+
batchNext = batches[index + 1];
427+
428+
if (batch.textures.length > 0)
429+
{
430+
for (textureIndex = 0; textureIndex < batch.textures.length; ++textureIndex)
431+
{
432+
nTexture = batch.textures[textureIndex];
433+
434+
if (nTexture)
435+
{
436+
renderer.setTexture2D(nTexture, 1 + textureIndex);
437+
}
438+
}
439+
440+
gl.activeTexture(gl.TEXTURE0);
441+
}
442+
443+
batchVertexCount = batchNext.first - batch.first;
444+
445+
if (batch.texture === null || batchVertexCount <= 0)
446+
{
447+
continue;
448+
}
449+
450+
renderer.setTexture2D(batch.texture, 0);
451+
452+
gl.drawArrays(topology, batch.first, batchVertexCount);
453+
}
454+
455+
// Left over data
456+
batch = batches[batches.length - 1];
457+
458+
if (batch.textures.length > 0)
459+
{
460+
for (textureIndex = 0; textureIndex < batch.textures.length; ++textureIndex)
461+
{
462+
nTexture = batch.textures[textureIndex];
463+
464+
if (nTexture)
465+
{
466+
renderer.setTexture2D(nTexture, 1 + textureIndex);
467+
}
468+
}
469+
470+
gl.activeTexture(gl.TEXTURE0);
471+
}
472+
473+
batchVertexCount = vertexCount - batch.first;
474+
475+
if (batch.texture && batchVertexCount > 0)
476+
{
477+
renderer.setTexture2D(batch.texture, 0);
478+
479+
gl.drawArrays(topology, batch.first, batchVertexCount);
480+
}
345481

346482
this.vertexCount = 0;
483+
484+
batches.length = 0;
485+
486+
this.pushBatch();
487+
347488
this.flushLocked = false;
348489

349490
return this;

0 commit comments

Comments
 (0)