constructor(styleOrShaders, variables, helper, enableHitDetection, filter) {
    /**
     * @private
     * @type {import('../../webgl/Helper.js').default}
     */
    this.helper_;

    /**
     * @private
     */
    this.hitDetectionEnabled_ = !!enableHitDetection;

    let asShaders = /** @type {AsShaders} */ (styleOrShaders);
    const isShaders = 'builder' in styleOrShaders;
    if (!isShaders) {
      const asRule = /** @type {AsRule} */ (styleOrShaders);
      const parseResult = parseLiteralStyle(
        asRule.style,
        variables,
        asRule.filter,
      );
      asShaders = {
        builder: parseResult.builder,
        attributes: parseResult.attributes,
        uniforms: parseResult.uniforms,
      };
    }

    /**
     * @private
     * @type {WebGLProgram}
     */
    this.fillProgram_;

    /**
     * @private
     * @type {WebGLProgram}
     */
    this.strokeProgram_;

    /**
     * @private
     * @type {WebGLProgram}
     */
    this.symbolProgram_;

    /**
     * @type {boolean}
     * @private
     */
    this.hasFill_ = !!asShaders.builder.getFillVertexShader();
    if (this.hasFill_) {
      /**
       * @private
       */
      this.fillVertexShader_ = asShaders.builder.getFillVertexShader();
      /**
       * @private
       */
      this.fillFragmentShader_ = asShaders.builder.getFillFragmentShader();
    }

    /**
     * @type {boolean}
     * @private
     */
    this.hasStroke_ = !!asShaders.builder.getStrokeVertexShader();
    if (this.hasStroke_) {
      /**
       * @private
       */
      this.strokeVertexShader_ = asShaders.builder.getStrokeVertexShader();
      /**
       * @private
       */
      this.strokeFragmentShader_ = asShaders.builder.getStrokeFragmentShader();
    }

    /**
     * @type {boolean}
     * @private
     */
    this.hasSymbol_ = !!asShaders.builder.getSymbolVertexShader();
    if (this.hasSymbol_) {
      /**
       * @private
       */
      this.symbolVertexShader_ = asShaders.builder.getSymbolVertexShader();
      /**
       * @private
       */
      this.symbolFragmentShader_ = asShaders.builder.getSymbolFragmentShader();
    }

    /**
     * @type {function(import('../../Feature.js').FeatureLike): boolean}
     * @private
     */
    this.featureFilter_ = null;
    if (filter) {
      this.featureFilter_ = this.computeFeatureFilter(filter);
    }

    const hitDetectionAttributes = this.hitDetectionEnabled_
      ? {
          hitColor: {
            callback() {
              return colorEncodeId(this.ref, tmpColor);
            },
            size: 4,
          },
        }
      : {};

    /**
     * @private
     */
    this.customAttributes_ = Object.assign(
      {},
      hitDetectionAttributes,
      asShaders.attributes,
    );
    /**
     * @private
     */
    this.uniforms_ = asShaders.uniforms;

    const customAttributesDesc = Object.entries(this.customAttributes_).map(
      ([name, value]) => ({
        name: `a_${name}`,
        size: value.size || 1,
        type: AttributeType.FLOAT,
      }),
    );
    /**
     * @type {Array<import('../../webgl/Helper.js').AttributeDescription>}
     * @private
     */
    this.polygonAttributesDesc_ = [
      {
        name: Attributes.POSITION,
        size: 2,
        type: AttributeType.FLOAT,
      },
      ...customAttributesDesc,
    ];
    /**
     * @type {Array<import('../../webgl/Helper.js').AttributeDescription>}
     * @private
     */
    this.lineStringAttributesDesc_ = [
      {
        name: Attributes.SEGMENT_START,
        size: 2,
        type: AttributeType.FLOAT,
      },
      {
        name: Attributes.MEASURE_START,
        size: 1,
        type: AttributeType.FLOAT,
      },
      {
        name: Attributes.SEGMENT_END,
        size: 2,
        type: AttributeType.FLOAT,
      },
      {
        name: Attributes.MEASURE_END,
        size: 1,
        type: AttributeType.FLOAT,
      },
      {
        name: Attributes.JOIN_ANGLES,
        size: 2,
        type: AttributeType.FLOAT,
      },
      {
        name: Attributes.DISTANCE,
        size: 1,
        type: AttributeType.FLOAT,
      },
      {
        name: Attributes.PARAMETERS,
        size: 1,
        type: AttributeType.FLOAT,
      },
      ...customAttributesDesc,
    ];
    /**
     * @type {Array<import('../../webgl/Helper.js').AttributeDescription>}
     * @private
     */
    this.pointAttributesDesc_ = [
      {
        name: Attributes.POSITION,
        size: 2,
        type: AttributeType.FLOAT,
      },
      {
        name: Attributes.INDEX,
        size: 1,
        type: AttributeType.FLOAT,
      },
      ...customAttributesDesc,
    ];

    this.setHelper(helper);
  }
@param {VectorStyle} styleOrShaders Literal style or custom shaders
@param {import('../../style/flat.js').StyleVariables} variables Style variables
@param {import('../../webgl/Helper.js').default} helper Helper
@param {boolean} [enableHitDetection] Whether to enable the hit detection (needs compatible shader)
@param {import("../../expr/expression.js").ExpressionValue} [filter] Optional filter expression