updateExecutorGroup_(tile, pixelRatio, projection) {
    const layer = /** @type {import("../../layer/VectorTile.js").default} */ (
      this.getLayer()
    );
    const revision = layer.getRevision();
    const renderOrder = layer.getRenderOrder() || null;

    const resolution = tile.wantedResolution;
    const builderState = tile.getReplayState(layer);
    if (
      !builderState.dirty &&
      builderState.renderedResolution === resolution &&
      builderState.renderedRevision == revision &&
      builderState.renderedRenderOrder == renderOrder
    ) {
      return;
    }

    const source = layer.getSource();
    const declutter = !!layer.getDeclutter();
    const sourceTileGrid = source.getTileGrid();
    const tileGrid = source.getTileGridForProjection(projection);
    const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);

    const sourceTiles = source.getSourceTiles(pixelRatio, projection, tile);
    const layerUid = getUid(layer);
    delete tile.hitDetectionImageData[layerUid];
    tile.executorGroups[layerUid] = [];
    builderState.dirty = false;
    for (let t = 0, tt = sourceTiles.length; t < tt; ++t) {
      const sourceTile = sourceTiles[t];
      if (sourceTile.getState() != TileState.LOADED) {
        continue;
      }
      const sourceTileCoord = sourceTile.tileCoord;
      const sourceTileExtent =
        sourceTileGrid.getTileCoordExtent(sourceTileCoord);
      const sharedExtent = getIntersection(tileExtent, sourceTileExtent);
      const builderExtent = buffer(
        sharedExtent,
        layer.getRenderBuffer() * resolution,
        this.tempExtent,
      );
      const bufferedExtent = equals(sourceTileExtent, sharedExtent)
        ? null
        : builderExtent;
      const builderGroup = new CanvasBuilderGroup(
        0,
        sharedExtent,
        resolution,
        pixelRatio,
      );
      const squaredTolerance = getSquaredRenderTolerance(
        resolution,
        pixelRatio,
      );

      /**
       * @param {import("../../Feature.js").FeatureLike} feature Feature.
       * @param {number} [index] Render order index.
       * @this {CanvasVectorTileLayerRenderer}
       */
      const render = function (feature, index) {
        let styles;
        const styleFunction =
          feature.getStyleFunction() || layer.getStyleFunction();
        if (styleFunction) {
          styles = styleFunction(feature, resolution);
        }
        if (styles) {
          const dirty = this.renderFeature(
            feature,
            squaredTolerance,
            styles,
            builderGroup,
            declutter,
            index,
          );
          builderState.dirty = builderState.dirty || dirty;
        }
      };

      const features = sourceTile.getFeatures();
      if (renderOrder && renderOrder !== builderState.renderedRenderOrder) {
        features.sort(renderOrder);
      }
      for (let i = 0, ii = features.length; i < ii; ++i) {
        const feature = features[i];
        if (
          !bufferedExtent ||
          intersects(bufferedExtent, feature.getGeometry().getExtent())
        ) {
          render.call(this, feature, i);
        }
      }
      const executorGroupInstructions = builderGroup.finish();
      // no need to clip when the render tile is covered by a single source tile
      const replayExtent =
        layer.getRenderMode() !== 'vector' &&
        declutter &&
        sourceTiles.length === 1
          ? null
          : sharedExtent;
      const renderingReplayGroup = new CanvasExecutorGroup(
        replayExtent,
        resolution,
        pixelRatio,
        source.getOverlaps(),
        executorGroupInstructions,
        layer.getRenderBuffer(),
        true,
      );
      tile.executorGroups[layerUid].push(renderingReplayGroup);
    }
    builderState.renderedRevision = revision;
    builderState.renderedRenderOrder = renderOrder;
    builderState.renderedResolution = resolution;
  }
@param {import("../../VectorRenderTile.js").default} tile Tile.
@param {number} pixelRatio Pixel ratio.
@param {import("../../proj/Projection.js").default} projection Projection.
@private