Skip to content

Commit 252a76f

Browse files
committed
The Headless render mode has been implemented. You can now set HEADLESS as the renderType in the Game Config and it will run a special game step that skips rendering. It will still create a Canvas element, as lots of internal systems (like input) rely on it, but it will not draw anything to it. Fix phaserjs#3256
1 parent a223b35 commit 252a76f

5 files changed

Lines changed: 83 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* Game.Config.roundPixels property added to prevent sub-pixel interpolation during rendering of Game Objects in WebGL and Canvas.
1010
* Load.plugin now accepts a class as an argument as well as a URL string (thanks @nkholski)
1111
* Tween.complete will allow you to flag a tween as being complete, no matter what stage it is at. If an onComplete callback has been defined it will be invoked. You can set an optional delay before this happens (thanks @Jerenaux for the idea)
12+
* The Headless render mode has been implemented. You can now set HEADLESS as the `renderType` in the Game Config and it will run a special game step that skips rendering. It will still create a Canvas element, as lots of internal systems (like input) rely on it, but it will not draw anything to it. Fix #3256 (thanks @rgk)
1213

1314
### Bug Fixes
1415

src/boot/CreateRenderer.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,27 @@ var CreateRenderer = function (game)
2525

2626
// Game either requested Canvas,
2727
// or requested AUTO or WEBGL but the browser doesn't support it, so fall back to Canvas
28-
if (config.renderType === CONST.CANVAS || (config.renderType !== CONST.CANVAS && !Features.webGL))
28+
29+
if (config.renderType !== CONST.HEADLESS)
2930
{
30-
if (Features.canvas)
31+
if (config.renderType === CONST.CANVAS || (config.renderType !== CONST.CANVAS && !Features.webGL))
3132
{
32-
// They requested Canvas and their browser supports it
33-
config.renderType = CONST.CANVAS;
33+
if (Features.canvas)
34+
{
35+
// They requested Canvas and their browser supports it
36+
config.renderType = CONST.CANVAS;
37+
}
38+
else
39+
{
40+
throw new Error('Cannot create Canvas or WebGL context, aborting.');
41+
}
3442
}
3543
else
3644
{
37-
throw new Error('Cannot create Canvas or WebGL context, aborting.');
45+
// Game requested WebGL and browser says it supports it
46+
config.renderType = CONST.WEBGL;
3847
}
3948
}
40-
else
41-
{
42-
// Game requested WebGL and browser says it supports it
43-
config.renderType = CONST.WEBGL;
44-
}
4549

4650
// Pixel Art mode?
4751
if (config.pixelArt)
@@ -78,6 +82,12 @@ var CreateRenderer = function (game)
7882
game.canvas.style.height = (config.height * config.zoom).toString() + 'px';
7983
}
8084

85+
if (config.renderType === CONST.HEADLESS)
86+
{
87+
// Nothing more to do here
88+
return;
89+
}
90+
8191
var CanvasRenderer;
8292
var WebGLRenderer;
8393

src/boot/DebugHeader.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,16 @@ var DebugHeader = function (game)
2525
return;
2626
}
2727

28-
var renderType = (config.renderType === CONST.CANVAS) ? 'Canvas' : 'WebGL';
28+
var renderType = 'WebGL';
29+
30+
if (config.renderType === CONST.CANVAS)
31+
{
32+
renderType = 'Canvas';
33+
}
34+
else if (config.renderType === CONST.HEADLESS)
35+
{
36+
renderType = 'Headless';
37+
}
2938

3039
var audioConfig = config.audio;
3140
var deviceAudio = game.device.audio;

src/boot/Game.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,14 @@ var Game = new Class({
294294

295295
this.config.postBoot();
296296

297-
this.loop.start(this.step.bind(this));
297+
if (this.renderer)
298+
{
299+
this.loop.start(this.step.bind(this));
300+
}
301+
else
302+
{
303+
this.loop.start(this.headlessStep.bind(this));
304+
}
298305

299306
VisibilityHandler(this.events);
300307

@@ -370,6 +377,45 @@ var Game = new Class({
370377
this.events.emit('postrender', renderer);
371378
},
372379

380+
/**
381+
* A special version of the Game Step for the HEADLESS renderer only.
382+
*
383+
* The main Game Step. Called automatically by the Time Step, once per browser frame (typically as a result of
384+
* Request Animation Frame, or Set Timeout on very old browsers.)
385+
*
386+
* The step will update the global managers first, then proceed to update each Scene in turn, via the Scene Manager.
387+
*
388+
* This process emits `prerender` and `postrender` events, even though nothing actually displays.
389+
*
390+
* @method Phaser.Game#headlessStep
391+
* @fires Phaser.Game#prerenderEvent
392+
* @fires Phaser.Game#postrenderEvent
393+
* @since 3.2.0
394+
*
395+
* @param {integer} time - The current timestamp as generated by the Request Animation Frame or SetTimeout.
396+
* @param {number} delta - The delta time elapsed since the last frame.
397+
*/
398+
headlessStep: function (time, delta)
399+
{
400+
// Global Managers
401+
402+
this.input.update(time, delta);
403+
404+
this.sound.update(time, delta);
405+
406+
// Scenes
407+
408+
this.onStepCallback();
409+
410+
this.scene.update(time, delta);
411+
412+
// Render
413+
414+
this.events.emit('prerender');
415+
416+
this.events.emit('postrender');
417+
},
418+
373419
/**
374420
* Game Pause event.
375421
*

src/gameobjects/components/Pipeline.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,11 @@ var Pipeline = {
5252
{
5353
var renderer = this.scene.sys.game.renderer;
5454

55-
if (renderer.gl && renderer.hasPipeline(pipelineName))
55+
if (renderer && renderer.gl && renderer.hasPipeline(pipelineName))
5656
{
5757
this.defaultPipeline = renderer.getPipeline(pipelineName);
5858
this.pipeline = this.defaultPipeline;
59+
5960
return true;
6061
}
6162

@@ -77,9 +78,10 @@ var Pipeline = {
7778
{
7879
var renderer = this.scene.sys.game.renderer;
7980

80-
if (renderer.gl && renderer.hasPipeline(pipelineName))
81+
if (renderer && renderer.gl && renderer.hasPipeline(pipelineName))
8182
{
8283
this.pipeline = renderer.getPipeline(pipelineName);
84+
8385
return true;
8486
}
8587

@@ -98,6 +100,7 @@ var Pipeline = {
98100
resetPipeline: function ()
99101
{
100102
this.pipeline = this.defaultPipeline;
103+
101104
return (this.pipeline !== null);
102105
},
103106

0 commit comments

Comments
 (0)