diff --git a/source/backend/qnn/backend/QNNPerf.cpp b/source/backend/qnn/backend/QNNPerf.cpp index 53090be464..356a8021bf 100644 --- a/source/backend/qnn/backend/QNNPerf.cpp +++ b/source/backend/qnn/backend/QNNPerf.cpp @@ -16,13 +16,30 @@ QNNPerf::QNNPerf(const QNN_INTERFACE_VER_TYPE * qnnInterface) { mQnnInterface = qnnInterface; QnnDevice_Infrastructure_t deviceInfra = nullptr; - CALL_QNN(mQnnInterface->deviceGetInfrastructure(&deviceInfra)); + auto infrastructureStatus = mQnnInterface->deviceGetInfrastructure(&deviceInfra); + if (infrastructureStatus != QNN_SUCCESS || deviceInfra == nullptr) { + MNN_PRINT("MNN_QNN: Failed to get HTP perf infrastructure, error:%lu infra=%p\n", + (unsigned long)infrastructureStatus, + deviceInfra); + return; + } QnnHtpDevice_Infrastructure_t *htpInfra = static_cast(deviceInfra); + if (htpInfra->infraType != QNN_HTP_DEVICE_INFRASTRUCTURE_TYPE_PERF || htpInfra->perfInfra.createPowerConfigId == nullptr) { + MNN_PRINT("MNN_QNN: HTP perf infrastructure is unavailable. infraType=%d createPowerConfigId=%p\n", + htpInfra->infraType, + htpInfra->perfInfra.createPowerConfigId); + return; + } mPerfInfra = htpInfra->perfInfra; uint32_t deviceId = 0; uint32_t coreId = 0; - CALL_QNN(mPerfInfra.createPowerConfigId(deviceId, coreId, &mPowerConfigId)); + auto powerConfigStatus = mPerfInfra.createPowerConfigId(deviceId, coreId, &mPowerConfigId); + if (powerConfigStatus != QNN_SUCCESS) { + MNN_PRINT("MNN_QNN: Failed to create HTP power config id, error:%lu\n", (unsigned long)powerConfigStatus); + mPowerConfigId = 0; + return; + } mPowerConfigBurst = { .option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_DCVS_V3, @@ -73,7 +90,9 @@ QNNPerf::QNNPerf(const QNN_INTERFACE_VER_TYPE * qnnInterface) { // destory power config QNNPerf::~QNNPerf() { - CALL_QNN(mPerfInfra.destroyPowerConfigId(mPowerConfigId)); + if (mPerfInfra.destroyPowerConfigId != nullptr && mPowerConfigId != 0) { + CALL_QNN(mPerfInfra.destroyPowerConfigId(mPowerConfigId)); + } } @@ -83,6 +102,9 @@ void QNNPerf::setRpcLatencyAndPolling() { ::memset(&rpcControlLatency, 0, sizeof(rpcControlLatency)); rpcControlLatency.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_RPC_CONTROL_LATENCY; rpcControlLatency.rpcControlLatencyConfig = 100; // use rpc control latency recommended 100 us, refer hexagon sdk + if (mPerfInfra.setPowerConfig == nullptr || mPowerConfigId == 0) { + return; + } const QnnHtpPerfInfrastructure_PowerConfig_t *powerConfigs1[] = {&rpcControlLatency, NULL}; CALL_QNN(mPerfInfra.setPowerConfig(mPowerConfigId, powerConfigs1)); // set RPC latency config on power config ID created @@ -101,11 +123,17 @@ void QNNPerf::setPowerConfigBurst() { #ifdef QNN_VERBOSE MNN_PRINT("MNN QNN set burst mode\n"); #endif + if (mPerfInfra.setPowerConfig == nullptr || mPowerConfigId == 0) { + return; + } const QnnHtpPerfInfrastructure_PowerConfig_t *powerConfigs[] = {&mPowerConfigBurst, NULL}; CALL_QNN(mPerfInfra.setPowerConfig(mPowerConfigId, powerConfigs)); } void QNNPerf::setPowerConfigBalanced() { + if (mPerfInfra.setPowerConfig == nullptr || mPowerConfigId == 0) { + return; + } const QnnHtpPerfInfrastructure_PowerConfig_t *powerConfigs[] = {&mPowerConfigBalanced, NULL}; CALL_QNN(mPerfInfra.setPowerConfig(mPowerConfigId, powerConfigs)); } diff --git a/source/backend/qnn/execution/QNNStridedSlice.cpp b/source/backend/qnn/execution/QNNStridedSlice.cpp index eb34daf0f5..cb29602083 100644 --- a/source/backend/qnn/execution/QNNStridedSlice.cpp +++ b/source/backend/qnn/execution/QNNStridedSlice.cpp @@ -92,15 +92,12 @@ ErrorCode QNNStridedSlice::onEncode(const std::vector &inputs, const s } return NO_ERROR; } - if (TensorUtils::getDescribe(inputs[0])->dimensionFormat == MNN_DATA_FORMAT_NC4HW4) { - MNN_ERROR("[QNN] Don't Support NC4HW4 stridedslice now\n"); - return NOT_SUPPORT; - } + bool isNC4HW4 = (TensorUtils::getDescribe(inputs[0])->dimensionFormat == MNN_DATA_FORMAT_NC4HW4); auto param = mOp->main_as_StridedSliceParam(); mNodeType = "StridedSlice"; - // Deal with ranges. + // Deal with ranges (in MNN's NCHW logical order). std::vector beginRaw(mInputDim, 0); std::vector endRaw = inputTensor->shape(); std::vector strideRaw(mInputDim, 1); @@ -110,6 +107,30 @@ ErrorCode QNNStridedSlice::onEncode(const std::vector &inputs, const s this->computeRangesType1(inputs, beginRaw, endRaw, strideRaw); } + // NC4HW4 layout: QNN sees NHWC, so remap ranges from NCHW → NHWC + if (isNC4HW4 && mInputDim >= 2) { + // NCHW order: [N, C, H, W, ...] → NHWC order: [N, H, W, ..., C] + // Save channel (axis=1) values + int beginC = beginRaw[1], endC = endRaw[1], strideC = strideRaw[1]; + // Shift spatial dims left: axis 2..N-1 → axis 1..N-2 + for (int i = 2; i < mInputDim; i++) { + beginRaw[i - 1] = beginRaw[i]; + endRaw[i - 1] = endRaw[i]; + strideRaw[i - 1] = strideRaw[i]; + } + // Channel becomes last + beginRaw[mInputDim - 1] = beginC; + endRaw[mInputDim - 1] = endC; + strideRaw[mInputDim - 1] = strideC; + + // Also remap endRaw to use NHWC shape from inputShape (already remapped above) + for (int i = 0; i < mInputDim; i++) { + if (endRaw[i] > inputShape[i]) { + endRaw[i] = inputShape[i]; + } + } + } + std::vector rangeData(mInputDim * 3, 0); for (int axis = 0; axis < mInputDim; axis++) { rangeData[3 * axis + 0] = beginRaw[axis]; @@ -166,21 +187,26 @@ void QNNStridedSlice::computeRangesType1(const std::vector &inputs, st auto inputTensor = inputs[0]; auto beginTensor = inputs[1]; auto endTensor = inputs[2]; - auto strideTensor = inputs[4]; auto beginRawSource = beginTensor->host(); auto endRawSource = endTensor->host(); - auto strideRawSource = strideTensor->host(); + + // fromType=1: inputs = [data, begin, end, axes, (stride)] + // stride is optional — when absent (4 inputs), default all strides to 1 + bool hasStride = (inputs.size() >= 5); + int* strideRawSource = nullptr; + if (hasStride) { + strideRawSource = inputs[4]->host(); + } auto axisTensor = inputs[3]; int sliceDim = beginTensor->length(0); - MNN_ASSERT(sliceDim == endTensor->length(0) && sliceDim == axisTensor->length(0) && sliceDim == strideTensor->length(0)); for (int i = 0; i < sliceDim; i++) { int tempAxis = axisTensor->host()[i]; tempAxis = tempAxis >= 0 ? tempAxis : (tempAxis + mInputDim); beginRaw[tempAxis] = CLIP(beginRawSource[i], 0, inputs[0]->length(tempAxis) - 1); endRaw[tempAxis] = CLIP(endRawSource[i], 1, inputs[0]->length(tempAxis)); - strideRaw[tempAxis] = strideRawSource[i]; + strideRaw[tempAxis] = hasStride ? strideRawSource[i] : 1; } return; } @@ -206,25 +232,43 @@ class QNNStridedSliceCreator : public QnnBackend::Creator { return new QNNStridedSlice(backend, op); } auto param = op->main_as_StridedSliceParam(); + if (nullptr == param) { + MNN_PRINT("MNN_QNN StridedSlice: param is null\n"); + return nullptr; + } + + MNN_PRINT("MNN_QNN StridedSlice: fromType=%d, inputs=%zu, beginMask=%d, endMask=%d, shrinkMask=%d, newAxisMask=%d, ellipsisMask=%d\n", + param->fromType(), inputs.size(), param->beginMask(), param->endMask(), + param->shrinkAxisMask(), param->newAxisMask(), param->ellipsisMask()); // , and should be static. for (int i = 1; i < inputs.size(); i++) { - MNN_ASSERT(TensorUtils::getDescribe(inputs[i])->usage == Tensor::InsideDescribe::Usage::CONSTANT); + if (TensorUtils::getDescribe(inputs[i])->usage != Tensor::InsideDescribe::Usage::CONSTANT) { + MNN_PRINT("MNN_QNN StridedSlice: input[%d] is NOT constant (usage=%d), skip\n", i, (int)TensorUtils::getDescribe(inputs[i])->usage); + return nullptr; + } } if (param->fromType() == 1) { - MNN_ASSERT(param->shrinkAxisMask() == 0 && param->newAxisMask() == 0 && param->ellipsisMask() == 0); - if (inputs.size() != 5) { + if (param->shrinkAxisMask() != 0 || param->newAxisMask() != 0 || param->ellipsisMask() != 0) { + MNN_PRINT("MNN_QNN StridedSlice: fromType1 unsupported masks\n"); + return nullptr; + } + // fromType=1: inputs = [data, begin, end, axes, (stride)] + // stride is optional — default to 1 when absent (4 inputs) + if (inputs.size() != 4 && inputs.size() != 5) { + MNN_PRINT("MNN_QNN StridedSlice: fromType1 inputs.size=%zu, need 4 or 5\n", inputs.size()); return nullptr; } return new QNNStridedSlice(backend, op); } - // [TODO] 把newAxisMask和ellipsisMask考虑在内 if (param->fromType() == 0) { if (inputs.size() == 4 && param->newAxisMask() == 0 && param->ellipsisMask() == 0) { return new QNNStridedSlice(backend, op); } else { + MNN_PRINT("MNN_QNN StridedSlice: fromType0 rejected: inputs=%zu, newAxisMask=%d, ellipsisMask=%d\n", + inputs.size(), param->newAxisMask(), param->ellipsisMask()); return nullptr; } }