"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.offsetToIndices = exports.copyArray = exports.generatePoolingCode = exports.WebGLMaxPool = exports.WebGLGlobalMaxPool = exports.WebGLAveragePool = exports.WebGLGlobalAveragePool = void 0;
const pool_1 = require("../../../ops/pool");
const util_1 = require("../../../util");
class WebGLGlobalAveragePool extends pool_1.GlobalAveragePool {
    run(inferenceHandler, inputs) {
        return inferenceHandler.run(this, inputs);
    }
    createProgramInfo(inferenceHandler, inputs) {
        return createAveragePoolProgramInfo(inferenceHandler, inputs, true, this.kernelShape, this.autoPad, this.strides, this.pads, this.countIncludePad);
    }
    createRunData(inferenceHandler, programInfo, inputs) {
        const inputTDs = [inferenceHandler.getOrCreateTextureData(inputs[0], programInfo.inputLayouts[0])];
        return {
            inputTextureDatas: inputTDs,
            outputTextureData: inferenceHandler.createTextureDataFromLayout(programInfo.outputLayout, inputTDs[0].tensor.type),
            uniformData: {}
        };
    }
}
exports.WebGLGlobalAveragePool = WebGLGlobalAveragePool;
class WebGLAveragePool extends pool_1.AveragePool {
    run(inferenceHandler, inputs) {
        return inferenceHandler.run(this, inputs);
    }
    createProgramInfo(inferenceHandler, inputs) {
        return createAveragePoolProgramInfo(inferenceHandler, inputs, false, this.kernelShape, this.autoPad, this.strides, this.pads, this.countIncludePad);
    }
    createRunData(inferenceHandler, programInfo, inputs) {
        const inputTDs = [inferenceHandler.getOrCreateTextureData(inputs[0], programInfo.inputLayouts[0])];
        return {
            inputTextureDatas: inputTDs,
            outputTextureData: inferenceHandler.createTextureDataFromLayout(programInfo.outputLayout, inputTDs[0].tensor.type),
            uniformData: {}
        };
    }
}
exports.WebGLAveragePool = WebGLAveragePool;
function createAveragePoolProgramInfo(inferenceHandler, inputs, isGlobalOperator, kernelShape = [], autoPad = '', strides = [], pads = [], countIncludePad) {
    const inputShape = inputs[0].dims.slice();
    util_1.PoolConvUtil.adjustPoolAttributes(isGlobalOperator, inputShape, kernelShape, strides, pads);
    const outputShape = util_1.PoolConvUtil.computePoolOutputShape(isGlobalOperator, inputShape, strides, kernelShape, pads, autoPad);
    const kernelSize = util_1.ShapeUtil.size(kernelShape);
    const op1 = 'value += _X(x);';
    let op2 = '';
    if (countIncludePad) {
        op2 += `value /= float(${kernelSize});`;
    }
    else {
        op2 += `value /= float(${kernelSize} - pad);`;
    }
    const inputLayout = inferenceHandler.getOrCreateTextureLayout(inputs[0]);
    const poolingCode = generatePoolingCode(inputLayout, kernelShape, pads, strides, op1, op2, '0.0');
    const shaderSource = `
      ${poolingCode}
    `;
    return {
        inputLayouts: [inputLayout],
        outputLayout: inferenceHandler.createTextureLayoutFromShape(outputShape),
        samplers: ['X'],
        shaderSource,
    };
}
class WebGLGlobalMaxPool extends pool_1.GlobalMaxPool {
    run(inferenceHandler, inputs) {
        return inferenceHandler.run(this, inputs);
    }
    createProgramInfo(inferenceHandler, inputs) {
        return createMaxPoolProgramInfo(inferenceHandler, inputs, true, this.kernelShape, this.autoPad, this.strides, this.pads);
    }
    createRunData(inferenceHandler, programInfo, inputs) {
        const inputTDs = [inferenceHandler.getOrCreateTextureData(inputs[0])];
        return {
            inputTextureDatas: inputTDs,
            outputTextureData: inferenceHandler.createTextureDataFromLayout(programInfo.outputLayout, inputTDs[0].tensor.type),
            uniformData: {}
        };
    }
}
exports.WebGLGlobalMaxPool = WebGLGlobalMaxPool;
class WebGLMaxPool extends pool_1.MaxPool {
    run(inferenceHandler, inputs) {
        return inferenceHandler.run(this, inputs);
    }
    createProgramInfo(inferenceHandler, inputs) {
        return createMaxPoolProgramInfo(inferenceHandler, inputs, false, this.kernelShape, this.autoPad, this.strides, this.pads);
    }
    createRunData(inferenceHandler, programInfo, inputs) {
        const inputTDs = [inferenceHandler.getOrCreateTextureData(inputs[0])];
        return {
            inputTextureDatas: inputTDs,
            outputTextureData: inferenceHandler.createTextureDataFromLayout(programInfo.outputLayout, inputTDs[0].tensor.type),
            uniformData: {}
        };
    }
}
exports.WebGLMaxPool = WebGLMaxPool;
function createMaxPoolProgramInfo(inferenceHandler, inputs, isGlobalOperator, kernelShape = [], autoPad = '', strides = [], pads = []) {
    const inputShape = inputs[0].dims.slice();
    util_1.PoolConvUtil.adjustPoolAttributes(isGlobalOperator, inputShape, kernelShape, strides, pads);
    const outputShape = util_1.PoolConvUtil.computePoolOutputShape(isGlobalOperator, inputShape, strides, kernelShape, pads, autoPad);
    const op1 = `
              value = max(_X(x), value);
      `;
    const op2 = '';
    const inputLayout = inferenceHandler.createTextureLayoutFromShape(inputShape);
    const poolingCode = generatePoolingCode(inputLayout, kernelShape, pads, strides, op1, op2, '-1e5');
    const shaderSource = `
    ${poolingCode}
  `;
    return {
        inputLayouts: [inputLayout],
        outputLayout: inferenceHandler.createTextureLayoutFromShape(outputShape),
        samplers: ['X'],
        shaderSource,
    };
}
function generatePoolingCode(x, kernelShape, pads, strides, op1, op2, startVal) {
    const inputDims = x.shape;
    const rank = x.shape.length;
    if (kernelShape.length <= 2) {
        const kw = kernelShape[kernelShape.length - 1];
        const sw = strides[strides.length - 1];
        const pwStart = pads[pads.length / 2 - 1];
        const pwEnd = pads[pads.length - 1];
        const dimW = inputDims[rank - 1];
        let codeW = '';
        let codeH = '';
        let codeHEnd = '';
        if (pwStart + pwEnd !== 0) {
            codeW = `
                for (int i = 0; i < ${kw}; i++) {
                  x[${rank} - 1] = indices[${rank} - 1] * ${sw} - ${pwStart} + i;
                  if (x[${rank} - 1] < 0 || x[${rank} - 1] >= ${dimW}) {
                    pad++;
                    continue;
                  }
                  ${op1}
                }`;
        }
        else {
            codeW = `
                for (int i = 0; i < ${kw}; i++) {
                  x[${rank} - 1] = indices[${rank} - 1] * ${sw} - ${pwStart} + i;
                  ${op1}
                }`;
        }
        if (kernelShape.length === 2) {
            const kh = kernelShape[kernelShape.length - 2];
            const sh = strides[strides.length - 2];
            const phStart = pads[pads.length / 2 - 2];
            const phEnd = pads[pads.length - 2];
            const dimH = inputDims[rank - 2];
            if (phStart + phEnd !== 0) {
                codeH = `
              for (int j = 0; j < ${kh}; j++) {
                x[${rank} - 2] = indices[${rank} - 2] * ${sh} - ${phStart} + j;
                if (x[${rank} - 2] < 0 || x[${rank} - 2] >= ${dimH}) {
                  pad+= ${kw};
                  continue;
                }
            `;
            }
            else {
                codeH = `
                for (int j = 0; j < ${kh}; j++) {
                  x[${rank} - 2] = indices[${rank} - 2] * ${sh} - ${phStart} + j;
                `;
            }
            codeHEnd = `
              }
            `;
        }
        const poolingCode = `
            float process(int indices[${rank}]) {
              int x[${rank}];
              copyVec(indices, x);

              float value = ${startVal};
              int pad = 0;
              ${codeH}
              ${codeW}
              ${codeHEnd}
              ${op2}
              return value;
            }
          `;
        return poolingCode;
    }
    else {
        const kernelSize = util_1.ShapeUtil.size(kernelShape);
        const kernelStrides = util_1.ShapeUtil.computeStrides(kernelShape);
        const stridesRank = kernelStrides.length;
        const padsRank = pads.length;
        const offsetToIndicesFunction = offsetToIndices(stridesRank);
        const copyInputDims = copyArray(inputDims, 'inputDims');
        const copyPads = copyArray(pads, 'pads');
        const copyKernelStrides = copyArray(kernelStrides, 'kernelStrides');
        const copyStrides = copyArray(strides, 'strides');
        const hasPads = pads.reduce((sum, cur) => sum + cur);
        let padCode = '';
        if (hasPads) {
            padCode = `
                if (x[j] >= inputDims[j] || x[j] < 0) {
                  pad++;
                  isPad = true;
                  break;
                }
              }
              if (!isPad) {
                ${op1}
              }`;
        }
        else {
            padCode = `
                  }
                  ${op1}`;
        }
        const poolingCode = `
            ${offsetToIndicesFunction}
            float process(int indices[${rank}]) {
                int x[${rank}];
                copyVec(indices, x);
                int offset[${stridesRank}];
                int pads[${padsRank}];
                int inputDims[${rank}];
                int kernelStrides[${stridesRank}];
                int strides[${stridesRank}];
                ${copyPads}
                ${copyInputDims}
                ${copyStrides}
                ${copyKernelStrides}

                float value = ${startVal};
                int pad = 0;
                bool isPad = false;
                for (int i = 0; i < ${kernelSize}; i++) {
                    offsetToIndices(i, kernelStrides, offset);
                    isPad = false;
                    for (int j = ${rank} - ${stridesRank}; j < ${rank}; j++) {
                      x[j] = indices[j] * strides[j - ${rank} + ${stridesRank}]
                        + offset[j - ${rank} + ${stridesRank}] - pads[j - 2];
                      ${padCode}
                }
                ${op2}

                return value;
            }`;
        return poolingCode;
    }
}
exports.generatePoolingCode = generatePoolingCode;
function copyArray(array, arrayName) {
    let block = '';
    for (let i = 0; i < array.length; i++) {
        block += `
      ${arrayName}[${i}] = ${array[i]};
    `;
    }
    return block;
}
exports.copyArray = copyArray;
function offsetToIndices(rank) {
    return `
    void offsetToIndices(int offset, int[${rank}] strides, out int[${rank}] indices) {
      if (${rank} == 0) {
        return;
      }
      for (int i = 0; i < ${rank} - 1; ++i) {
        indices[i] = offset / strides[i];
        offset -= indices[i] * strides[i];
      }
      indices[${rank} - 1] = offset;
    }`;
}
exports.offsetToIndices = offsetToIndices;
//# sourceMappingURL=pool.js.map