Navigation

Phaser 3 Dev Log - August 2020

Published on 24th August 2020

Hi everyone! So, after a nice week on holiday, with plenty of messing around in the sea with my kids, I came back and have been working solidly on Phaser 3 since then. I just wanted to publish a little dev update to talk about what's new and to encourage you to try out the fresh beta release.

A couple of weeks ago, just prior to leaving for my vacation, I published Phaser 3.50 Beta 1. This was an evolution of the 3.24 beta I released previously in the July Dev Log, which I'd urge you to read first if you want to catch up on things.

Some of you may be wondering why the massive jump in version numbers? It's because while working on 3.24 I realized that really, this was the opportunity I needed to make some pretty big changes and I needed a way to signify that to you, the Phaser devs. I had already added multi-texture support and a brand new lights system but the changes just kept on coming, with hundreds of quality-of-life enhancements across the API.

It became obvious to me that if anyone was using some of Phasers more advanced features like Custom Pipelines, that 3.24 was fundamentally going to break everything for them. I need a way to flag this, without relying on just the Change Log, so I figured that a big leap in version number should at least make regular Phaser devs sit-up and take notice.

Hence, the move from 3.24 to 3.50. Samme joked on Discord that it was 'halfway between Phaser 3 and Phaser 4' :) which isn't far from the truth, to be honest. There's a shed-load of new features in v3 that are there because of the work I've done in v4 and it's pleasing to me that each iteration of Phaser keeps empowering the next and sometimes the previous one too.

We Got New Tubes

One of the biggest changes in 3.50 is the way WebGL pipelines are handled and named. In order to make things clearer and just less verbose, I have renamed the pipelines as follows:

The Texture Tint Pipeline is now called the Multi Pipeline.
The Texture Tint Strip Pipeline is now called the Rope Pipeline.
The Forward Diffuse Light Pipeline (phew) is now called the Light Pipeline.

The Bitmap Mask Pipeline remains unchanged because it was the only one with a sensible name to start with :)

There is a brand new pipeline, too, called the Single Pipeline. Where-as the Multi Pipeline is all about multi-texture support, the Single Pipeline is all about... well... single textures. It's a lot closer to the old Texture Tint Pipeline and means if you currently extend that in your game, you can just swap to the Single Pipeline and you won't have to change any of your shader code. Yay! Of course, you won't benefit from multi-texture support, either, but at least it's now up to you which path to take (convert or just extend the Single Pipeline).

Here's an example of using the new Single Pipeline. I've left out the actual shader source for brevity:

As you can see, it's identical to the old way of using a pipeline, just with a different class to extend.

You can run this fully-featured example of a custom pipeline extending the Single Pipeline.

The new Multi Pipeline requires a little more work in your shaders to get running, but not a lot. The way in which you extend the pipeline is exactly the same as any other pipeline:

I have fully documented the configuration object you pass to the pipeline, so it properly auto-completes in TypeScript now (and just makes life better for everyone!), essentially, however, at the minimum you just pass in a new fragment shader to use. The difference with the Multi Pipeline is that because it's set to use multi-textures, you need to include a couple of special tags in your shader source.

The first is `%count%`, which will be replaced with the maximum number of supported textures for the browser + GPU. This is used for the sampler2D uniform arrays:

The second is `%forloop%`. This will be replaced with a block of glsl code that determines the sampler2D to read the texture from. For example:

To finish the shader, you also need to declare a varying:

This name is used specifically in the block of glsl that is generated for the for loop. When the pipeline is created, your fragment shader is passed through a new function `Utils.parseFragmentShaderMaxTextures`. This will search for the two tags in the source and replace them with the relevant glsl code. The resulting auto-generated glsl code for the above looks like this:

Again, here is a Labs example of extending the Multi Pipeline.

The size of the conditional block will change based on how many textures are supported, or configured in the render config if you wish to override this.

This parsing happens automatically. If the tags don't exist in your fragment shader code, it passes through unchanged to the shader compiler. This means you've three options. 1 - You can include the tags in your code and let Phaser modify the glsl for you. 2 - You can pass your shader got through the `parseFragmentShaderMaxTextures` function in advance, before creating your pipeline, or 3 - your shader code includes the required if-else blocks already.

There are some cases where you may prefer to use option 3. For example, WebGL has to provide 8 texture units as a minimum, so you could set the 'maximum' to be 8 and be guaranteed to get this quantity, no matter what system you're on. You can then hand-craft your shaders with this number in mind.

Multi-texturing handled via shader conditionals is a double-edged sword. On modern GPUs, you can easily have 16, or more texture units supported. This results in heavy branching in the shader code itself, as 16 if-else conditions are tested. For a single-threaded system running on the CPU, such as JavaScript, this would be of little significance. 16 checks are quite a few, but equally easily managed. However, GPUs are massively parallel, with many asynchronous shader cores, with each core designed to run multiple threads and incoherent branching on GPUs carries a penalty, even on the most modern of GPUs. However, without multi-texture support in this way, the GPU has to flush the batch and load textures far more frequently, which in itself is inefficient and has a significant cost.

Ultimately, performance testing on your target devices is definitely required. You may find that if your game uses lots of textures and large batches, that using multi-textures is going to dramatically improve GPU performance. I believe this to be the most common scenario, which is why it's enabled by default in 3.50. If you do find performance to be an issue, and you've done all you can to mitigate this, such as using texture atlases and carefully structuring your Scenes, then you should also look at perhaps limiting the total number of max textures, in order to reduce branching in the shaders. This is very game-specific, so it's up to you to determine the best course of action. At least, though, with 3.50 you now have this option to add to your arsenal.

Geometry Intersection

Also arriving in 3.50 Beta 2 are a collection of new geometry intersection functions. These are:

Geom.Intersects.GetLineToLine - Returns a Vector3 containing the point of intersection between 2 line segments, with the `z` property holding the distance value.

Geom.Intersects.GetLineToPolygon - Checks for the closest point of intersection between a line segment and an array of polygons.

Geom.Intersects.GetLineToPoints - Checks for the closest point of intersection between a line segment and an array of points, where each pair of points form a line segment.

Geom.Intersects.GetRaysFromPointToPolygon - Emits rays out from the given point and detects for intersection against all given polygons, returning the points of intersection in the results array.

Have a look at this video https://youtu.be/1S8Ck1rXlm8 to see these functions in operation.

As you can see, by using a combination of some geometry primitives and these new functions it's possible for you to create a shadow system directly from Phaser. Combined with the new lights pipeline this should give you a lot more flexibility than ever before.

You'll also see in the video the new Polygon Simplify function in action. This can dramatically reduce the number of points in a polyline, making it super-useful for 'calming down' generated data or tracing complex areas into potential shadow maps.

I'll write more about this in the next Dev Report, or the 3.50 release report, whichever comes first!

Download v3.50 Beta 2

As usual, there are loads more changes in 3.50 than I've touched upon here and the Change Log is well worth looking at. Beta 2 contains lots of fixes over Beta 1, so if you hit any issues with Beta 1, please give Beta 2 a go. The new v3.50 Beta 2 is available from both npm and GitHub.

You can get it from npm using the beta tag:

Or download it from here https://github.com/photonstorm/phaser/releases/tag/v3.50.0-beta.2

You'll find pre-built bundles to download, or you can check out the master branch and build yourself.

Note that it does not have updated TypeScript defs yet, so if you want to use those, please pull down the repo and use `npm run tsgen` to build new ones locally.

If you find an issue report it to me either on Discord, or (even better) open it as an issue on GitHub and tag it 3.50 Beta 2.

I need to take a couple of days off this week to attend a funeral, so I'm not anticipating 3.50 released this week, plus I want to give you all some time to experiment with it, too. However, I can't see any reason why we won't have a release during the first week of September. After this, I'll be returning to Phaser 4 development in anger.