From d121a1f9069625ded5b2466e7efe66f9307a1104 Mon Sep 17 00:00:00 2001 From: Hector Li Date: Tue, 7 May 2024 11:35:29 -0700 Subject: [PATCH] Enable int32 data support for Clip (#20590) Enable int32 data support for Clip fix issue: https://github.com/microsoft/onnxruntime/issues/20525 --- .../qnn/builder/opbuilder/clip_op_builder.cc | 54 +++++++++++++--- .../test/providers/qnn/clip_op_test.cc | 61 +++++++++++++------ 2 files changed, 86 insertions(+), 29 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/opbuilder/clip_op_builder.cc b/onnxruntime/core/providers/qnn/builder/opbuilder/clip_op_builder.cc index dc99687e78..e5dc4d04af 100644 --- a/onnxruntime/core/providers/qnn/builder/opbuilder/clip_op_builder.cc +++ b/onnxruntime/core/providers/qnn/builder/opbuilder/clip_op_builder.cc @@ -43,15 +43,51 @@ static Status ProcessClipMinMax(QnnModelWrapper& qnn_model_wrapper, std::vector val_bytes; ORT_RETURN_IF_ERROR(qnn_model_wrapper.GetTensorInfo(input, input_info)); assert(input_info.is_initializer); // Checked by ExplicitOpCheck(). - if (QNN_DATATYPE_FLOAT_16 == input_info.qnn_data_type) { - ORT_RETURN_IF_ERROR(qnn_model_wrapper.UnpackInitializerData(*input_info.initializer_tensor, val_bytes)); - MLFloat16 fp16_value = *reinterpret_cast(val_bytes.data()); - float_value = fp16_value.ToFloat(); - } else { - ORT_RETURN_IF_NOT(QNN_DATATYPE_FLOAT_32 == input_info.qnn_data_type, - "QNN EP: The 'min' input of the Clip operator must be of type float32."); - ORT_RETURN_IF_ERROR(qnn_model_wrapper.UnpackInitializerData(*input_info.initializer_tensor, val_bytes)); - float_value = *reinterpret_cast(val_bytes.data()); + ORT_RETURN_IF_ERROR(qnn_model_wrapper.UnpackInitializerData(*input_info.initializer_tensor, val_bytes)); + switch (input_info.qnn_data_type) { + case QNN_DATATYPE_INT_8: { + float_value = static_cast(*reinterpret_cast(val_bytes.data())); + break; + } + case QNN_DATATYPE_INT_16: { + float_value = static_cast(*reinterpret_cast(val_bytes.data())); + break; + } + case QNN_DATATYPE_INT_32: { + float_value = static_cast(*reinterpret_cast(val_bytes.data())); + break; + } + case QNN_DATATYPE_INT_64: { + float_value = static_cast(*reinterpret_cast(val_bytes.data())); + break; + } + case QNN_DATATYPE_UINT_8: { + float_value = static_cast(*val_bytes.data()); + break; + } + case QNN_DATATYPE_UINT_16: { + float_value = static_cast(*reinterpret_cast(val_bytes.data())); + break; + } + case QNN_DATATYPE_UINT_32: { + float_value = static_cast(*reinterpret_cast(val_bytes.data())); + break; + } + case QNN_DATATYPE_UINT_64: { + float_value = static_cast(*reinterpret_cast(val_bytes.data())); + break; + } + case QNN_DATATYPE_FLOAT_16: { + MLFloat16 fp16_value = *reinterpret_cast(val_bytes.data()); + float_value = fp16_value.ToFloat(); + break; + } + case QNN_DATATYPE_FLOAT_32: { + float_value = *reinterpret_cast(val_bytes.data()); + break; + } + default: + return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "min/max input data type not supported."); } return Status::OK(); diff --git a/onnxruntime/test/providers/qnn/clip_op_test.cc b/onnxruntime/test/providers/qnn/clip_op_test.cc index e899f870f9..c3a75fd744 100644 --- a/onnxruntime/test/providers/qnn/clip_op_test.cc +++ b/onnxruntime/test/providers/qnn/clip_op_test.cc @@ -17,16 +17,17 @@ namespace test { // Runs a model with a Clip operator on the QNN CPU backend. Checks the graph node assignment // and that inference outputs for QNN EP and CPU EP match. template -static void RunClipTestOnCPU(const TestInputDef& input_def, - const std::vector>& min_max_defs, - ExpectedEPNodeAssignment expected_ep_assignment, - int opset = 13) { +static void RunClipTest(const TestInputDef& input_def, + const std::vector>& min_max_defs, + ExpectedEPNodeAssignment expected_ep_assignment, + bool on_cpu_backend = true, + int opset = 13) { ProviderOptions provider_options; #if defined(_WIN32) - provider_options["backend_path"] = "QnnCpu.dll"; + provider_options["backend_path"] = on_cpu_backend ? "QnnCpu.dll" : "QnnHtp.dll"; #else - provider_options["backend_path"] = "libQnnCpu.so"; + provider_options["backend_path"] = on_cpu_backend ? "libQnnCpu.so" : "libQnnHtp.so"; #endif RunQnnModelTest(BuildOpTestCase("Clip", {input_def}, min_max_defs, {}), @@ -42,29 +43,29 @@ static void RunClipTestOnCPU(const TestInputDef& input_def, // Test that Clip with a dynamic min or max input is not supported by QNN EP. TEST_F(QnnCPUBackendTests, Clip_Dynamic_MinMax_Unsupported) { // Dynamic min input is not supported. - RunClipTestOnCPU(TestInputDef({1, 3, 4, 4}, false, -10.0f, 10.0f), - {TestInputDef({}, false /* is_initializer */, {-5.0f})}, - ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP. + RunClipTest(TestInputDef({1, 3, 4, 4}, false, -10.0f, 10.0f), + {TestInputDef({}, false /* is_initializer */, {-5.0f})}, + ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP. // Dynamic max input is not supported. - RunClipTestOnCPU(TestInputDef({1, 3, 4, 4}, false, -10.0f, 10.0f), - {TestInputDef({}, true, {-5.0f}), - TestInputDef({}, false, {5.0f})}, - ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP. + RunClipTest(TestInputDef({1, 3, 4, 4}, false, -10.0f, 10.0f), + {TestInputDef({}, true, {-5.0f}), + TestInputDef({}, false, {5.0f})}, + ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP. } // Test Clip with default min/max. TEST_F(QnnCPUBackendTests, Clip_4D_f32_DefaultMinMax) { - RunClipTestOnCPU(TestInputDef({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)), - {}, // Don't specify min/max inputs. - ExpectedEPNodeAssignment::All); + RunClipTest(TestInputDef({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)), + {}, // Don't specify min/max inputs. + ExpectedEPNodeAssignment::All); } // Test Clip with 5D input. TEST_F(QnnCPUBackendTests, Clip_5D_f32) { - RunClipTestOnCPU(TestInputDef({1, 1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)), - {TestInputDef({}, true, {-5.0f}), - TestInputDef({}, true, {5.0f})}, - ExpectedEPNodeAssignment::All); + RunClipTest(TestInputDef({1, 1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)), + {TestInputDef({}, true, {-5.0f}), + TestInputDef({}, true, {5.0f})}, + ExpectedEPNodeAssignment::All); } #if defined(__aarch64__) || defined(_M_ARM64) || defined(__linux__) @@ -72,6 +73,26 @@ TEST_F(QnnCPUBackendTests, Clip_5D_f32) { // HTP tests: // +// Test Clip with float32 on HTP +TEST_F(QnnHTPBackendTests, Clip_f32) { + bool on_cpu_backend = false; + RunClipTest(TestInputDef({1, 1, 3, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 12)), + {TestInputDef({}, true, {-5.0f}), + TestInputDef({}, true, {5.0f})}, + ExpectedEPNodeAssignment::All, + on_cpu_backend); +} + +// Test Clip with int32 on HTP +TEST_F(QnnHTPBackendTests, Clip_int32) { + bool on_cpu_backend = false; + RunClipTest(TestInputDef({1, 1, 3, 2}, false, {1, 2, -5, 3, -10, 25}), + {TestInputDef({}, true, {-5}), + TestInputDef({}, true, {5})}, + ExpectedEPNodeAssignment::All, + on_cpu_backend); +} + // Runs a QDQ Clip model on the QNN (HTP) EP and the ORT CPU EP. Checks the graph node assignment and that inference // running the QDQ model on QNN EP is at least as accurate as on ORT CPU EP (compared to the baseline float32 model). template