rich

Phaser 2.7: New WebGL Tilemap Renderer (Beta 3)

Recommended Posts

Update: Beta 3 is now ready. This fixes the setStageReference fix, along with others.

Update: Phaser 2.7.0 Beta 2 is now out, with loads of Tilemap updates (see post below)

I'm pleased to announce that we have completed our work on the WebGL Tilemap Renderer for Phaser, and it's now available for testing in Phaser 2.7.0 Beta 1.

For too long tilemap performance in Phaser has been poor running under WebGL. With a Canvas to Texture internal drawing system, coupled with super-expensive texture uploads, it never took advantage of WebGL at all, and caused significant performance issues.

Thanks to the support of our Phaser Patreon backers, we were able to fund the development of a brand new WebGL Tilemap renderer. The new renderer creates 'internal' layers for each different tileset included in a map, which permits high-speed batch drawing via WebGL, and fixes large tiles at map edges, and other glitches, which are visible in the Canvas implementation.

When a map is processed, the system creates a list of tiles for each layer. This intermediate step allows us to continue to use all the existing map functionality, and get out a list which can be rapidly processed into TRIANGLE_STRIP batches.

The lists (there is one per layer) are processed into batch data which is fired up to the new WebGL shader, where it is drawn extremely rapidly and efficiently. The shader handles alpha and scaling on a per-batch basis. So the Tile.alpha value is ignored but the TilemapLayerGL.alpha is used, which is the same as the Canvas implementation.

Please Help Test!

You can download pre-built versions of Phaser 2.7.0 from the GitHub dev branch. This is a drop-in replacement for any game using Tilemaps under WebGL. Note that 2.7.0 is an upgraded version of Phaser 2.6.1, so if your game doesn't yet run under 2.6.1 you'll need to remedy that, before you can test out the new tilemaps.

Download Phaser 2.7.0 Beta

Once you've had a chance to test it, please report your findings in this thread. We're especially interested to know what performance you get on low-end GPUs or mobile hardware.
 

Share this post


Link to post
Share on other sites

In my (small) tilemap game the tiles are black and I'm getting WebGL errors:

Tilemap.createBlankLayer: Layer with matching name already exists

8 of those. My tilemap has 5 graphical layers and 4 object layers, not sure where that number is coming from. 256 of these before "too many errors, no more errors will be reported to the console for this context":

[.CommandBufferContext.Offscreen-MainThread-0x7f96f283a560]GL ERROR :GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 0

 

Share this post


Link to post
Share on other sites

@drhayes each layer needs a unique name, you are creating nine layers (5+4) and I'm guessing you're giving them all the same name, so you get 8 errors.

Please let me know if the webgl errors continue after you change the naming (which can be as simple as just adding a number to the end of each layer name).

It looks like this is original Phaser code, so I think you should have got the same error using the previous Canvas tilemaps... have you tried your game running in Canvas mode, and did it work with previous versions of Phaser?

 

Share this post


Link to post
Share on other sites

@PRSolucoes (sorry I can't easily create those glyphs on my keyboard, and copy/paste wanted to bring the whole text format with it!) - the new tile system does not affect your keyboard or mouse input at all.  Perhaps Rich or another user might be able to help you with that problem.  It is probable that other things changed as well for this release but I am only responsible for the webgl tile drawing :)

 

Share this post


Link to post
Share on other sites

Just to add that we're aware that rotated and / flipped tiles don't render correctly, and are looking into it. Also the same with camera shake effect not working (although it doesn't work on any shader, but we ought to support it on the tilemaps).

I'm looking in to the multi-layer issue today.

Share this post


Link to post
Share on other sites

@drhayes apologies!  I didn't realise that it has been generating those error messages from internal layers.  Rich has fixed that.

In the meantime the current dev branch contains updated tile renderer which should now handle 'rotated' tiles (they're actually flipped and/or diagonally flipped but the effect is the same as a rotation!), and multiple tilesets/multiple layers.  I've also added the screen shake functionality and layer resize is working... NOTE: at the moment in webgl mode you need to read the displayWidth/displayHeight properties to see what size it is set to... width/height as used by Canvas will only give you the map dimensions in pixels.  This means that the resize example doesn't work properly as it is using width < 800 as a criteria for permitting changes.

 

Share this post


Link to post
Share on other sites

Using Phaser 2.7.0 Beta 2:

Uncaught TypeError: Cannot read property 'tileWidth' of null
    Phaser.TilemapLayerGL @ phaser.js:98793
    createBlankLayer @ phaser.js:95954

I'm loading a tilemap, adding two tilesets, createLayer() x3, then createBlankLayer() which errors.

createBlankLayer() is calling Phaser.TilemapLayerGL with a null tileset:

        if (this.game.renderType === Phaser.WEBGL)
        {
            output = new Phaser.TilemapLayerGL(this.game, this, this.layers.length - 1, w, h, null);
        }

TilemapLayerGL() has some code that is suppose to check for an undefined tileset, but it's commented out. If I remove the comments and add '|| tileset === null' to the if statement, it works for my use case. When I change states, I get a 'child.removeStageReference is not a function' which I'll dig into later and is probably not related to tilemap.

Informally, my FPS was ~48-58 when walking around this tilemap with 2.6.1. It's now ~57-60 FPS.

 

 

Share this post


Link to post
Share on other sites

Hey Rich,

thanks for awesome PhaserJS over all this time! :)

I've got one error and one issue with 2.7.beta tile maps, detailed description following:
Beta 1 worked, Beta 2 throws an error:

phaser.2.7.js:20 TypeError: b.removeStageReference is not a function(…) minified
=> phaser.2.7.js:74623 TypeError: child.removeStageReference is not a function(…)

f.onload@phaser.2.7.js: 20

             try {
 if (xhr.readyState == 4 && xhr.status >= 400 && xhr.status <= 599) { // Handle HTTP status codes of 4xx and 5xx as errors, even if xhr.onerror was not called.
                    return onerror.call(_this, file, xhr);
                }
                else {
                    return onload.call(_this, file, xhr);
                }
            } catch (e) {

                //  If this was the last file in the queue and an error is thrown in the create method
                //  then it's caught here, so be sure we don't carry on processing it

                if (!_this.hasLoaded)
                {
                    _this.asyncComplete(file, e.message || 'Exception');
                }
                else
                {
                    if (window['console'])
                    {
                        console.error(e);
                    }
                }
            }

code hadn't changed since I tested Beta 1 .

Performance on camera movement in beta 1
I also seen a huge frame drop by moving the camera in beta one. In WebGL mode a bigger game had run stable at 60fps but moving the camera dropped it to nearly 20. This was reproducible by moving the camera which is following a sprite and has a leap of 0.1

The code to reproduce it:
extended camera class:
 

"use strict";
class CONTROLS_CAMERA extends Phaser.Sprite {
  constructor ( controls ) {
    super( game, 0, 0 );
    game.add.existing(this);
    this.width = game.width;
    this.height = game.height;
    this.data.boundingH = this.height / 2;
    this.data.boundingW = this.width / 2;
    this.controls = controls;
    game.physics.arcade.enable(this);
    this.anchor.setTo( 0.5 );
    this.body.allowGravity = false;
    this.body.collideWorldBounds = true;
    this.settings = {
      "speed" : 10
    }
    game.camera.follow(this);
    game.camera.lerp = { x: 0.1, y: 0.1 };
  }
update () {

    if ( this.controls.right() || game.input.mousePointer.x > game.width - 30) {
      if( this.x + this.data.boundingW < game.world.width )
      {
        this.x += this.settings.speed;

      }
    }else if ( this.controls.left() || game.input.mousePointer.x < 30 ) {
      if ( this.x - this.data.boundingW > 0 )
      {
        this.x -= this.settings.speed;

      }

    }
    if ( this.controls.up() || game.input.mousePointer.y < 30 ) {
      if ( this.y - this.data.boundingH > 0 )
      {
        this.y -= this.settings.speed;

      }

    }else if ( this.controls.down() || game.input.mousePointer.y > game.height - 30 ){
      if ( this.y + this.data.boundingH < game.world.height )
      {
        this.y += this.settings.speed;

      }

    }

  }

I hope that helps, if you need anything- just tell me.
You are doing an awesome job! That can not be said often enough!

regards
 

Share this post


Link to post
Share on other sites

I just exchanged the Phaser version (2.6.1 to 2.7 beta 1) in my own game, and that is the performance I was talking about.
=> The thing I tested was setting up a tile map 2000x2000 with 3 layers and using the camera-follow script I posted above.

Frames dropping to around 20. with v2.6. they were more stable (50-60). (2.6.1 vs 2.7 beta1)
- I have not tested with beta 2 since I got the reference error and had no time to have a deeper look on that.

regards

Share this post


Link to post
Share on other sites

Beta 3 resolved my child.removeStageReference issue.

Now it's back to tileset errors.

phaser.js:98911 Uncaught TypeError: Cannot read property 'tileWidth' of null

    Phaser.TilemapLayerGL @ phaser.js:98911

    createBlankLayer @ phaser.js:

If I change line 98875 to check for 'tileset === null' this level will load and work because there are tilesets in the map, but my next level fails because it calls game.add.tilemap(), sets the map.key followed by map.create(name. mapwidth, mapheight, tilewidth, tilesize) so tileset ends up being set to null on line 98887 which will cause the cw/ch assignments to fail or eventually line 98941 where baseTexture is set to a null image. If I change the order of my code to map.addTilesetImage() before calling map.create() the level will load and the tilemap looks corrupted - most tiles missing and the ones that show are really small. The corruption is a problem in 2.6.1 as well and goes away if I addTilesetImage() after creating my layers.

Then I see a lot of:

[.CommandBufferContext.Offscreen-MainThread-0x7ffaf3d116b0]GL ERROR :GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 0

I have three different tilemap levels:

works -> #1: game.add.tilemap(json tilemap), addTilesetImage x2, createLayer x3, createBlankLayer x2

works -> #2: game.add.tilemap(json tilemap), addTilesetImage, createlayer x 3

errors -> #3: game.add.tilemap(), addTilesetImage x2, map.create(with params), createBlankLayer x3

 

 

 

 

 

Edited by bml
Noted that the tilemap corruption was in 2.6.1 as well.

Share this post


Link to post
Share on other sites

Thanks for working on this feature!

I'm using tilemaps made with Tiled, and the 2.7 Beta 3 doesn't show anything. No error-messages. With 2.5 it works fine. 

It only shows the tilemap-layer when the game is set to Phaser.CANVAS, but then it doesn't have the correct x/y position.
Phaser.AUTO doesn't show anything.

Code used to create tilemap-layer:


  createLevelTilemap() {
    this.levelTilemap = game.add.tilemap(this.levelName);
    this.levelTilemap.addTilesetImage('Level', 'levelSpritesheet');
    this.levelBackgroundLayer = this.levelTilemap.createLayer('Level');
    this.levelBackgroundLayer.scale.set(this.levelSpriteScale);
    this.levelBackgroundLayer.resize(this.levelHeight * this.levelSpriteScale * this.levelTilesX, this.levelWidth * this.levelSpriteScale * this.levelTilesY);
    this.levelBackgroundLayer.fixedToCamera = false;
    this.levelBackgroundLayer.x = this.levelOffsetX;
    this.levelBackgroundLayer.y = this.levelOffsetY;
  }

In this state I calculate the scale depending on how many tiles there are in both dimensions and the browser-width/height, then I center the tilemap-layer and scale it accordingly.

UPDATE:
When I don't set fixedToCamera, the tilemap-position is fine on CANVAS, but then the performance is horrible, so I would prefer to stay in the WEBGL-Mode.

Share this post


Link to post
Share on other sites

I not it's only slightly - but it really makes a huge difference if the whole game looks like this (picture).

WebGL mode is blurry in comparison to CANVAS even if you use: image-rendering: pixelated;

You can notice this difference slightly in the image attached. have a look at the right jumper and the upper corner of the circle. there you can see what I mean.
Is this due to the webGL rendering itself? I haven't noticed until yet. It makes a huge difference since every graphic is effected by this blur in WebGL mode.

canvas-webgl-comparison.png

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.