将 mapbox-gl 放在一个 worker 中,并使用 readpixels() 读取以在主线程中编码

put mapbox-gl in a worker and read with readpixels() to encode in the main thread

提问人:palomino 提问时间:11/16/2023 更新时间:11/16/2023 访问量:31

问:

这是我第一次在 Stackoverflow 上提问,这是因为我真的被卡住了。我在 mapbox 中有一个动画,它与 h264-mp4-encoder 视频编码器在同一线程上运行。我的代码可以工作并完成工作,但是在渲染地图框动画期间,您会看到小的跳跃,有时还会出现振动,我确信这是因为这两个任务都是在同一线程中执行的。我试图创建一个用于编码的工作器,但我有错误,因为编码器需要 mapbox 实例。我正在研究offscreencanvas,但关于使用webgl的信息并不多。也许你可以推荐一种不同的方法来处理这个问题,要么将编码器发送给一个工作线程,要么将映射框发送给另一个工作线程,或者如果你打开了一个更好的方法来解决这个问题,那就更好了。

 map.on("load", async () => {
        // add 3d, sky and fog
        add3D();
        await map.once("idle");
        // don't forget to enable WebAssembly SIMD in chrome://flags for faster encoding
        const supportsSIMD = await simd();
        // initialize H264 video encoder
        const Encoder = await loadEncoder({ simd: supportsSIMD });
        const gl = map.painter.context.gl;
        const width =
          gl.drawingBufferWidth % 2 === 0
            ? gl.drawingBufferWidth
            : gl.drawingBufferWidth - 1;
        const height =
          gl.drawingBufferHeight % 2 === 0
            ? gl.drawingBufferHeight
            : gl.drawingBufferHeight - 1;

        //create encoder
        const encoder = Encoder.create({
          width,
          height,
          fps: 30,
          kbps: 2200,
          rgbFlipY: true,
        });
        // stub performance.now for deterministic rendering per-frame (only available in dev build)
        let now = performance.now();
        //mapboxgl.setNow(now);
        const ptr = encoder.getRGBPointer(); // keep a pointer to encoder WebAssembly heap memory
        function frame() { //this function is called to read the pixels every time mapbox re-renders
          //increment stub time by 16.6ms (60 fps)
          now += 1000 / 30;
          mapboxgl.setNow(now);
          const pixels = encoder.memory().subarray(ptr); // get a view into encoder memory
          gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); // read pixels into encoder
          console.log("inicia");
          encoder.encodeRGBPointer(); // encode the frame
        }
        map.on("render", frame); // set up frame-by-frame

        await playAnimations(geojson);//a call is made to the map animation function
        // stop recording
        map.off("render", frame);
        mapboxgl.restoreNow();
        setIsRunAnimate(false);
        //encoded video
        const mp4Blob = encoder.end();
        setVideoBlob(mp4Blob);
})

这是一个相信的工人,认为他可以实现它。我尝试的是读取工作器内部的像素并对其进行编码,但它给了我一个错误,因为它需要 mapboxgl。

// encoderWorker.js

let encoding = false;
let now = performance.now();
let ptr;

onmessage = async (event) => {
  const { width, height, fps, kbps, rgbFlipY } = event.data;

  const { simd } = await import("wasm-feature-detect");
  const Encoder = await import("../../assets/convertidor.js");

  const encoder = Encoder.create({
    width,
    height,
    fps,
    kbps,
    rgbFlipY,
  });

  ptr = encoder.getRGBPointer();

  function encodeFrame() {
    now += 1000 / fps;
    mapboxgl.setNow(now);
    const pixels = encoder.memory().subarray(ptr);
    encoder.encodeRGBPointer();
  }

  onmessage = (event) => {
    const { type } = event.data;
    if (type === "start" && !encoding) {
      // Start coding/frame-by-frame
      encoding = true;
      encodeFrame();
      requestAnimationFrame(encodeFrame);
    } else if (type === "finish" && encoding) {
      // Stop encryption
      encoding = false;

      // Return the encoded blob to the main thread
      const encodedBlob = encoder.end();
      postMessage({ type: "complete", encodedBlob });
    }
  };
};
WebGL Mapbox Web-Worker 视频编码 offscreen-canvas

评论

0赞 Kaiido 11/16/2023
我将使用两个 ArrayBuffers,一个您将从画布写入,然后传输到 Worker 线程,然后再将其复制到 WASM 缓冲区,最后传输回主线程,以便您可以编写下一个画布帧。
0赞 palomino 11/16/2023
那么我是否必须将渲染保留在主线程上?在进行测试时,我看到导致问题的唯一行是:gl.readPixels(0, 0, width, height, gl.RGBA,gl。UNSIGNED_BYTE,像素);将像素读取到编码器中。还有:encoder.encodeRGBPointer();在对这两行进行评论时,动画完全正常进行。readpixels() 进程是动画中影响最大的进程。
0赞 Kaiido 11/16/2023
我想,您也可以将其移动到另一个工作线程,我不知道mapbox,也不知道他们的API是否与上下文无关。但是,是的,假设执行所有操作(包括 readPixel)但编码工作正常,那么您需要在不同的线程中分离编码和映射框 (+ pixelRead)。为此,您需要将用于读取画布像素的缓冲区复制到 WASM 缓冲区中(因为我们无法传输 WASM 缓冲区)。

答: 暂无答案