Offscreen-Canvas not rendering some of the time

816 views Asked by At

I am trying to implement a in browser raster drawing plugin for the leaflet library that that extends the leaflets GridLayer api. Essentially for every tile there is function createTile that returns a canvas with some drawing on it. and leaflet shows the tile in correct position.

    
    initialize: function(raster_data){


        this.raster_data = raster_data;
        
    },

    createTile: function (tile_coords) {

        let _tile = document.createElement('canvas');
        
        let _tile_ctx = _tile.getContext('2d');

        // do some drawing here with values from this.raster_data

        return _tile;
    }

This implementation is so far working fine. Than I thought of offloading drawing with offscreen-canvas in a webworker. so I restructured the code like this

    
    initialize: function(raster_data){


        this.raster_data = raster_data;
        this.tile_worker = new Worker('tile_renderer.js')
        
    },

    createTile: function (tile_coords) {

        let _tile = document.createElement('canvas').transferControlToOffscreen();
        
        this.tile_worker.postMessage({
            _tile: _tile,
            _raster_data: this.raster_data
        },[_tile])

        

        return _tile;
    }

This works but every now and then i see a canvas that is just blank. That thing is quite random I don't know start from where and how should I debug this. can this be a problem that I am using a single worker for rendering every tile? any help is appreciated. Here is an example of a blank canvas. example blank canvas

1

There are 1 answers

0
Kaiido On BEST ANSWER

This a known bug: https://crbug.com/1202481

The issue appears when too many OffscreenCanvases are sent to the Worker serially.

The workaround is then to batch send all these OffscreenCanvases in a single call to postMessage().
In your code you could achieve this by storing all the objects to be sent and use a simple debouncing strategy using a 0 timeout to send them all at once:

createTile: function (tile_coords) {

  let _tile = document.createElement('canvas');

  _tile.setAttribute('width', 512);
  _tile.setAttribute('height', 512);

  let _off_tile = _tile.transferControlToOffscreen();


  this.tiles_to_add.push( _off_tile ); // store for later
  clearTimeout( this.batch_timeout_id ); // so that the callback is called only once
  this.batch_timeout_id = setTimeout( () => { 
    // send them all at once
    this.tileWorker.postMessage( { tiles: this.tiles_to_add }, this.tiles_to_add );
    this.tiles_to_add.length = 0;
  });

  return _tile;

}

Live example: https://artistic-quill-tote.glitch.me/