编辑渲染上下文时,Web Worker 中的离屏画布不更新

问题描述 投票:0回答:1

context:我正在使用名为 openglobus 的映射库来渲染 3d 行星。 Openglobus 有一个图块层,您可以在其中提供一个生成图块的函数,该函数可以返回画布。所以我制作的是一个函数,它生成这些画布以渲染为地球仪上的地图图层。为了保持高性能,该函数是异步的,并调用 Webworker 来执行实际的图块生成。因此画布是由图块生成器函数在主线程中制作的。然后画布将其控制权转移到屏幕外。接下来,网络工作者将获取所有必需的参数。当网络工作人员完成时,画布将应用于地球仪。

Tile 生成器函数如下所示(WM 代表 Worker Manager,它将请求分配给多个 Worker):

export default function BaseLayer(WM) {
  return new layer.CanvasTiles(`cnv`, {
    isBaseLayer: true,
    drawTile: function (material, applyCanvas) {
      const { tileZoom, tileX, tileY } = material.segment;
      const extent = material.segment.getExtentLonLat();
      const res = 700;

      const cnv = document.createElement("canvas");
      cnv.width = res;
      cnv.height = res;

      const offscreen = cnv.transferControlToOffscreen();

      WM.makeBaseTile({
        extent,
        tileX,
        tileY,
        tileZoom,
        res,
        offscreen,
      }).then(_ => applyCanvas(cnv));
    },
  });
}

Workers 在页面加载时创建并且不会被销毁。它们被重复用于经常使用的每个图块和缓存数据。绘制这些图块的工作代码的结构如下(简化):

self.onmessage = async (e) => {
  const { id, extent, tileX, tileY, tileZoom, res, offscreen } = e.data;

  const cnv = offscreen
  const ctx = cnv.getContext("2d");

  // draw fallback background color
  ctx.fillStyle = g.biomeColors[13]; //"#85a478";
  ctx.fillRect(0, 0, res, res);

  // Fetching data from online resources to draw
  let [biomeRes, waterRes, hillshade] = await Promise.all([...axios get requests]);

  // before water is added, draw the color of the biome (background)
  biomeRes.data.forEach((BIOME) => {
    BIOME.forEach((b) => {
      // custom function to draw geometries onto canvasses
      drawShape(ctx, b, color, "#000000", e.data, false, 0, false);
    });
  });

  // if water is an empty array, there is no water in this tile
  if (waterRes.length) {
    // jiggle is a custom function to add noise to geometries
    const water = jiggle(waterRes, extent, tileZoom, 3, 2);

    water.forEach((p) =>
      drawShape(ctx, p, "#5b99a6", "#2d4d54", e.data, false, g.outline)
    );
    }
  }

  // toggle tile borders on/off
  if (true) {
    ctx.beginPath();
    ctx.rect(0, 0, cnv.width, cnv.height);
    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    ctx.stroke();
    ctx.closePath();

    ctx.font = "50px serif";
    ctx.fillStyle = "black";
    ctx.fillText(`${tileZoom}`, 10, 60);
  }

  // draw hillshade, but blend by multiply to avoid white background
  // make opacity fade out when it reaches zoom level of 15, start at 10
  const img = await createImageBitmap(await hillshade.blob());
  ctx.globalCompositeOperation = "multiply";
  ctx.globalAlpha = Math.max(0, 1 - (tileZoom - 10) / 5);
  ctx.drawImage(img, 0, 0, res, res);
  ctx.globalCompositeOperation = "source-over";
  ctx.globalAlpha = 1.0;

  // ctx.commit();
  postMessage({ origin: JSON.stringify([id, tileX, tileY]) });

问题:当我以这种方式编码时,我遇到了一些图块未加载的问题。有些人这样做,但有些人则不这样做。它发生在 Chrome 和 Firefox 上,所以我认为它与这篇文章这个现有的 Chrome 错误(该错误也已标记为已修复)无关。

这个问题似乎在生成时间较长的复杂图块上最为普遍。但这都是异步的,所以我不明白为什么这可能是原因。

不过我确实找到了解决办法。这就是为什么我认为这不是 openglobus 库的问题,而是屏幕外画布的工作方式的问题。通过在工作进程结束时在 OffscreenCanvasRenderingContext2D 上使用 commit() 函数,我得到了一个完美的工作示例:

但是此提交功能仅适用于 Safari 和 Firefox 浏览器,不适用于基于 Chrome(ium) 的浏览器。有谁能更好地理解为什么在没有提交功能的情况下会发生这种情况,或者如何在 Chrome 中修复此问题?我已经找了一段时间了,但似乎没有相当于提交功能的东西。

javascript google-chrome canvas web-worker offscreen-canvas
1个回答
0
投票

控制

<canvas>
已发送给工作人员的占位符
OffscreenCanvas
的内容仅在工作人员的“更新渲染”步骤中更新。您需要等待“更新渲染”发生才能访问占位符上的内容
<canvas>

从你的问题来看,它是如何使用的,或者 Promise 等待什么,有点不清楚,但基本上有两种方法可以规避这个问题:

  • 继续使用占位符

    <canvas>
    并等待下一个绘画帧,例如在您的 Worker 中,您将在
    requestAnimationFrame()
    回调中执行绘图,然后通过
    postMessage()
    通知您的主线程,这应该在更新完成后在下一个任务中处理。

  • 请勿使用占位符

    <canvas>
    ,而是使用
    OffscreenCanvas
     方法将 
    ImageBitmap
    内容传输到
    transferToImageBitmap()
    对象,该方法将强制同步渲染到
    ImageBitmap
    中。最好的方法是直接使用返回的
    ImageBitmap
    提供库的代码,但如果它确实需要使用
    <canvas>
    元素,那么您可以从该元素 (ImageBitmapRenderingContext
    ) 创建一个 
    getContext("bitmaprenderer")
     并提供给它
    ImageBitmap
    (
    ctx.transferFromImageBitmap(bmp)
    )。

© www.soinside.com 2019 - 2024. All rights reserved.