_createIndexMap() {
    if (this.indexMap) {
      return
    }

    const inputRows = this.inputShape[0]
    const inputCols = this.inputShape[1]
    const [nbFilter, nbRow, nbCol] = this.kernelShape

    const [paddingRowBefore, paddingRowAfter, paddingColBefore, paddingColAfter] = this.outputPadding

    const effectiveKernelSize = (nbRow - this.strides[0] + 1) * (nbCol - this.strides[1] + 1)
    const indicesMapShape = [this.outputShape[0], this.outputShape[1], effectiveKernelSize]
    const indicesMapShapePadded = [
      this.outputShape[0] + paddingRowBefore + paddingRowAfter,
      this.outputShape[1] + paddingColBefore + paddingColAfter,
      effectiveKernelSize
    ]
    const outputRowIndicesMap = new Tensor([], indicesMapShape, { type: Int32Array })
    const outputColIndicesMap = new Tensor([], indicesMapShape, { type: Int32Array })
    const outputRowIndicesMapPadded = new Tensor([], indicesMapShapePadded, { type: Int32Array })
    const outputColIndicesMapPadded = new Tensor([], indicesMapShapePadded, { type: Int32Array })
    ops.assigns(outputRowIndicesMap.tensor, -1)
    ops.assigns(outputColIndicesMap.tensor, -1)
    ops.assigns(outputRowIndicesMapPadded.tensor, -1)
    ops.assigns(outputColIndicesMapPadded.tensor, -1)

    const matMulColIndicesPatch = new Tensor([], [nbRow, nbCol, nbFilter], { type: Int32Array })
    for (let i = 0; i < nbRow * nbCol * nbFilter; i++) {
      matMulColIndicesPatch.tensor.data[i] = i
    }

    for (let i = 0; i < inputRows; i++) {
      for (let j = 0; j < inputCols; j++) {
        const matMulRowIndex = i * inputCols + j
        const iOutPos = i * this.strides[0]
        const jOutPos = j * this.strides[1]
        assignToRowIndicesMap(
          outputRowIndicesMapPadded.tensor
            .hi(iOutPos + nbRow, jOutPos + nbCol, effectiveKernelSize)
            .lo(iOutPos, jOutPos, 0),
          matMulRowIndex,
          effectiveKernelSize
        )
        assignToColIndicesMap(
          outputColIndicesMapPadded.tensor
            .hi(iOutPos + nbRow, jOutPos + nbCol, effectiveKernelSize)
            .lo(iOutPos, jOutPos, 0),
          matMulColIndicesPatch.tensor.pick(null, null, 0),
          effectiveKernelSize
        )
      }
    }

    // remove padding
    ops.assign(
      outputRowIndicesMap.tensor,
      outputRowIndicesMapPadded.tensor
        .hi(this.outputShape[0] + paddingRowBefore, this.outputShape[1] + paddingColBefore, effectiveKernelSize)
        .lo(paddingRowBefore, paddingColBefore, 0)
    )
    ops.assign(
      outputColIndicesMap.tensor,
      outputColIndicesMapPadded.tensor
        .hi(this.outputShape[0] + paddingRowBefore, this.outputShape[1] + paddingColBefore, effectiveKernelSize)
        .lo(paddingRowBefore, paddingColBefore, 0)
    )

    // combine first two dimensions
    const tiledIndicesMapShape = [this.outputShape[0] * this.outputShape[1], effectiveKernelSize]
    this.indexMap = new Tensor([], tiledIndicesMapShape, { type: Int32Array })
    const channelData = new Tensor([], [effectiveKernelSize], { type: Int32Array })
    for (let i = 0; i < this.outputShape[0]; i++) {
      for (let j = 0; j < this.outputShape[1]; j++) {
        for (let k = 0; k < effectiveKernelSize; k++) {
          // i * cols + j
          const rowIndex = outputRowIndicesMap.tensor.get(i, j, k)
          const colIndex = outputColIndicesMap.tensor.get(i, j, k)
          if (rowIndex !== -1 && colIndex !== -1) {
            channelData.tensor.set(k, rowIndex * this.weights['kernel'].glTextureShape[1] + colIndex)
          } else {
            channelData.tensor.set(k, -1)
          }
        }
        ops.assign(this.indexMap.tensor.pick(i * this.outputShape[1] + j, null), channelData.tensor)
      }
    }

    this.indexMap.createGLTexture({ type: '2d', format: 'int', supportsTextureFragments: true })
  }
In GPU mode, we work directly on 2D-reshaped representations of the tensors. After the matrix multiply step produce
matrix Y, the final output Z at coordinate [i,j] will be the summation of a number of elements of the matrix Y.
Here, we calculate the indices of matrix Y for each coordinate [i,j] of Z, and encode these index maps as texture
arrays.