// // QNNPool.cpp // MNN // // Created by MNN on b'2025/04/10'. // Copyright © 2018, Alibaba Group Holding Limited // #include "QNNPool.hpp" namespace MNN { namespace QNN { #ifdef ENABLE_QNN_ONLINE_FINALIZE ErrorCode QNNPool::onEncode(const std::vector &inputs, const std::vector &outputs) { // Params: filter_size([h, w]), stride([h, w]), pad_amount([[height_pad_before, height_pad_after], [width_pad_before, width_pad_after]]), count_pad_for_edges(false), rounding_mode mParams.clear(); mInputs.clear(); mOutputs.clear(); if (mOp->type() == OpType_Pooling3D) { return this->onEncode3D(inputs, outputs); } mNodeType = "PoolAvg2d"; std::vector filterSizeData(2); std::vector strideData(2); std::vector padAmountData(4); uint32_t roundingMode; setParamPool(mNodeType, filterSizeData, strideData, padAmountData, roundingMode, inputs[0], outputs[0]); // shape(out[0])[height_out] = ROUND((pad_amount[0,0] + shape(in[0])[height] + pad_amount[0,1] - filter_size[0]) / stride[0] + 1) if(inputs[0]->height() < filterSizeData[0]) { filterSizeData[0] = inputs[0]->height(); } if(inputs[0]->width() < filterSizeData[1]) { filterSizeData[1] = inputs[0]->width(); } this->createParamTensor("filter_size", QNN_DATATYPE_UINT_32, {2}, (void *)filterSizeData.data()); this->createParamTensor("stride", QNN_DATATYPE_UINT_32, {2}, (void *)strideData.data()); this->createParamTensor("pad_amount", QNN_DATATYPE_UINT_32, {2, 2}, (void *)padAmountData.data()); if (mOp->main_as_Pool()->type() == PoolType_AVEPOOL) { bool countType = mOp->main_as_Pool()->countType() ? true : false; this->createParamScalar("count_pad_for_edges", countType); } this->createParamScalar("rounding_mode", roundingMode); #ifdef QNN_VERBOSE MNN_PRINT("QNN Pool input:"); auto shape0 = inputs[0]->shape(); for(int i = 0; i < shape0.size(); i++) { MNN_PRINT("%d x ", shape0[i]); } MNN_PRINT("\noutput:"); auto outShape = outputs[0]->shape(); for(int i = 0; i < outShape.size(); i++) { MNN_PRINT("%d x ", outShape[i]); } MNN_PRINT("\n"); MNN_PRINT("mNodeType:%s, filterSizeData:%dx%d, strideData:%dx%d, padAmountData:%dx%dx%dx%d, roundingMode:%d\n", mNodeType.c_str(), \ filterSizeData[0], filterSizeData[1], strideData[0], strideData[1], padAmountData[0], padAmountData[1], padAmountData[2], padAmountData[3], roundingMode); #endif this->addNodeCommon(inputs, outputs); return NO_ERROR; } ErrorCode QNNPool::onEncode3D(const std::vector &inputs, const std::vector &outputs) { auto input = inputs[0]; if (input->dimensions() != 4) { MNN_QNN_NOT_SUPPORT_SPECIAL_CASE; } auto pool3D = mOp->main_as_Pool3D(); mNodeType = (pool3D->type() == PoolType_AVEPOOL) ? "PoolAvg2d" : "PoolMax2d"; std::vector filterSizeData(2); std::vector strideData(2); std::vector padAmountData(4); uint32_t roundingMode; uint32_t * inputDim = mBackend->getNativeTensor(inputs[0])->v1.dimensions; uint32_t height = inputDim[1]; uint32_t width = inputDim[2]; if (pool3D->isGlobal()) { filterSizeData[0] = height; filterSizeData[1] = width; strideData[0] = height; strideData[1] = width; padAmountData[0] = 0; padAmountData[1] = 0; padAmountData[2] = 0; padAmountData[3] = 0; roundingMode = 1; // or makes no difference. } else { MNN_QNN_NOT_SUPPORT_SPECIAL_CASE; } this->createParamTensor("filter_size", QNN_DATATYPE_UINT_32, {2}, (void *)filterSizeData.data()); this->createParamTensor("stride", QNN_DATATYPE_UINT_32, {2}, (void *)strideData.data()); this->createParamTensor("pad_amount", QNN_DATATYPE_UINT_32, {2, 2}, (void *)padAmountData.data()); if (pool3D->type() == PoolType_AVEPOOL) { // bool countType = mOp->main_as_Pool()->countType() ? true : false; this->createParamScalar("count_pad_for_edges", false); } this->createParamScalar("rounding_mode", roundingMode); this->addNodeCommon(inputs, outputs); return NO_ERROR; } void QNNPool::setParamPool(std::string & nodeType, std::vector & filterSizeData, std::vector & strideData, std::vector & padAmountData, uint32_t & roundingMode, Tensor * input, Tensor * output) { auto pool = mOp->main_as_Pool(); nodeType = (pool->type() == PoolType_AVEPOOL) ? "PoolAvg2d" : "PoolMax2d"; if (pool->isGlobal()) { filterSizeData[0] = input->height(); filterSizeData[1] = input->width(); strideData[0] = input->height(); strideData[1] = input->width(); padAmountData[0] = 0; padAmountData[1] = 0; padAmountData[2] = 0; padAmountData[3] = 0; roundingMode = 1; // or makes no difference. return; } filterSizeData[0] = pool->kernelY(); filterSizeData[1] = pool->kernelX(); strideData[0] = pool->strideY(); strideData[1] = pool->strideX(); if (pool->padType() == PoolPadType_SAME) { int padNeededWidth = (output->width() - 1) * strideData[1] + filterSizeData[1] - input->width(); int padNeededHeight = (output->height() - 1) * strideData[0] + filterSizeData[0] - input->height(); auto padLeft = padNeededWidth / 2; auto padTop = padNeededHeight / 2; auto padRight = padNeededWidth - padLeft; auto padBottom = padNeededHeight - padTop; padAmountData[0] = padTop; padAmountData[1] = padBottom; padAmountData[2] = padLeft; padAmountData[3] = padRight; roundingMode = 1; // ceil return; } if (pool->padType() == PoolPadType_VALID) { padAmountData[0] = 0; padAmountData[1] = 0; padAmountData[2] = 0; padAmountData[3] = 0; roundingMode = 0; // floor return; } if (nullptr != pool->pads()) { MNN_ASSERT(pool->pads()->size() == 4); padAmountData[0] = pool->pads()->data()[0]; padAmountData[1] = pool->pads()->data()[2]; padAmountData[2] = pool->pads()->data()[1]; padAmountData[3] = pool->pads()->data()[3]; } else { padAmountData[0] = pool->padY(); padAmountData[1] = pool->padY(); padAmountData[2] = pool->padX(); padAmountData[3] = pool->padX(); } roundingMode = (pool->ceilModel()) ? 1 : 0; return; } class QNNPoolCreator : public QnnBackend::Creator { public: virtual QNNCommonExecution * onCreate(const std::vector& inputs, const std::vector& outputs, const MNN::Op* op, Backend* backend) const override { return new QNNPool(backend, op); } }; REGISTER_QNN_OP_CREATOR(QNNPoolCreator, OpType_Pooling) REGISTER_QNN_OP_CREATOR(QNNPoolCreator, OpType_Pooling3D) #endif } // end namespace QNN } // end namespace MNN