context: I am using a mapping library called openglobus to render a 3d planet. Openglobus has a tile layer, where you can give a function that generates tiles, that function can return canvasses. So what I made is a function that generates these canvasses to get rendered as a map layer on that globe. To keep things performant, this function is asynchronous and calls upon a webworker to do the actual tile generation. So the canvas calls the webworker in the main thread by the tile generator function. The webworker than receives the info it needs and makes an image. And when the web worker finishes, a (pointer to the) ImageBitmaps is returned.
Tile generator function looks like this (WM stands for WorkerManager that distributes requests over the multiple workers):
export default function ObjectLayer(WM, level) {
return new layer.CanvasTiles(`cnv`, {
isBaseLayer: false,
height: level * 1 + 0.2,
minZoom: 12,
opacity: 0.9,
drawTile: function (material, applyCanvas) {
const { tileZoom, tileX, tileY } = material.segment;
const extent = material.segment.getExtentLonLat();
const res = 600;
WM.makeObjectTile({
extent,
tileX,
tileY,
tileZoom,
res,
level
}).then(applyCanvas);
},
});
}
Workers are created when the page loads and are not destroyed. they are reused for every tile and cache data that is used often. Worker code that draws these tiles is structured like this (simplified):
self.onmessage = async (e) => {
const { id, extent, tileX, tileY, tileZoom, res, level } = e.data;
// create offscreen canvas with size res x res
const cnv = new OffscreenCanvas(res, res);
// filter the data to be shown on the tile
const data = allData.filter((d) => {
} if (d.geometry.type == "Polygon") {
return booleanIntersects(d.geometry, tileGeom)
} else if (d.geometry.type == "LineString") {
return booleanIntersects(d.geometry, tileGeom)
}
return false
});
// draw all assets
if (level == 1) {
data.forEach((d) => {
const asset = PlayerAssets[d.type];
asset.draw(asset, ctx, d, td, level, pixelRes);
});
}
// convert the canvas to an image bitmap and post it back to the main thread with the transferToImageBitmap() method
const imgBitmap = cnv.transferToImageBitmap();
postMessage({ origin: JSON.stringify([id, tileX, tileY, level]), imgBitmap }, [imgBitmap]);
};
When the message is posted back, the WorkerManager (WM) makes sure the image data gets back to the original caller of the function. All of this was also part of an earlier issue that went into the best way of returning the image from the worker to the main thread. Converting the canvas to an imageBitmap was the conclusion.
problem: This works fine, Images are returned properly and get displayed. But when checking for chrome, there is a lot of lag and the main thread freezes (dropped frames) or sometimes even crashes to a black screen during moments where a lot of tiles should finish rendering. The main thread freezes but in the profiler the main thread is practically empty. It appears as if transferring the bitmap using cnv.transferToImageBitmap() and posting that result back to the main thread goes not as smooth. On Firefox however, this is not an issue.
Is there something in the implementation of this that could cause this many dropped frames on Chrome, but not on Firefox?