_create2DRowFragmentedGLTexture() {
    const gl = webgl2.context
    const textureOptions = webgl2.getWebGLTextureOptions(this.glTextureType, this.glTextureFormat)
    const { textureTarget, textureInternalFormat, textureFormat, textureType } = textureOptions

    this.glTextureFragments = []
    this.glTextureFragmentShape = [MAX_TEXTURE_SIZE, this.glTextureShape[1]]
    const shape = this.glTextureFragmentShape
    const numFragments = Math.ceil(this.glTextureShape[0] / MAX_TEXTURE_SIZE)
    let offset = 0

    for (let k = 0; k < numFragments; k++) {
      const glTexture = gl.createTexture()
      webgl2.storeRef('texture', glTexture)
      gl.bindTexture(textureTarget, glTexture)

      // append 0s to last fragment
      let data
      if (k === numFragments - 1) {
        data = new this.arrayType(shape[0] * shape[1])
        data.set(this.tensor.data.slice(offset, offset + shape[0] * shape[1]), 0)
      } else {
        data = this.tensor.data.slice(offset, offset + shape[0] * shape[1])
      }
      gl.texImage2D(textureTarget, 0, textureInternalFormat, shape[1], shape[0], 0, textureFormat, textureType, data)

      // clamp to edge
      gl.texParameteri(textureTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
      gl.texParameteri(textureTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
      // no interpolation
      gl.texParameteri(textureTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
      gl.texParameteri(textureTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST)

      this.glTextureFragments.push(glTexture)
      offset += shape[0] * shape[1]
    }
  }
For 2D WebGL2 texture with first dimension exceeding MAX_TEXTURE_SIZE, creates as array of 2D textures