diff --git a/onnxruntime/core/optimizer/qdq_transformer/selectors_actions/shared/utils.cc b/onnxruntime/core/optimizer/qdq_transformer/selectors_actions/shared/utils.cc index f87a819387..f725bc40e5 100644 --- a/onnxruntime/core/optimizer/qdq_transformer/selectors_actions/shared/utils.cc +++ b/onnxruntime/core/optimizer/qdq_transformer/selectors_actions/shared/utils.cc @@ -80,7 +80,8 @@ static const OpVersionsAndSelector::OpVersionsMap GetBinaryOpVersionsMap() { {"Div", {}}, {"Mul", {}}, {"Pow", {}}, - {"Sub", {}}}; + {"Sub", {}}, + {"GridSample", {}}}; } static const OpVersionsAndSelector::OpVersionsMap GetVariadicOpVersionsMap() { return {{"Concat", {}}}; diff --git a/onnxruntime/core/providers/qnn/builder/op_builder_factory.cc b/onnxruntime/core/providers/qnn/builder/op_builder_factory.cc index 9c00b0faba..58ac3ad45a 100644 --- a/onnxruntime/core/providers/qnn/builder/op_builder_factory.cc +++ b/onnxruntime/core/providers/qnn/builder/op_builder_factory.cc @@ -63,6 +63,8 @@ OpBuilderRegistrations::OpBuilderRegistrations() { CreateSimpleOpBuilder("DepthToSpace", *this); CreateSimpleOpBuilder("SpaceToDepth", *this); + + CreateSimpleOpBuilder("GridSample", *this); } { diff --git a/onnxruntime/core/providers/qnn/builder/opbuilder/base_op_builder.h b/onnxruntime/core/providers/qnn/builder/opbuilder/base_op_builder.h index a21424c264..14d5e45799 100644 --- a/onnxruntime/core/providers/qnn/builder/opbuilder/base_op_builder.h +++ b/onnxruntime/core/providers/qnn/builder/opbuilder/base_op_builder.h @@ -120,6 +120,7 @@ class BaseOpBuilder : public IOpBuilder { {"Sub", QNN_OP_ELEMENT_WISE_SUBTRACT}, {"Tanh", QNN_OP_TANH}, {"Transpose", QNN_OP_TRANSPOSE}, + {"GridSample", QNN_OP_GRID_SAMPLE}, {"DequantizeLinear", QNN_OP_DEQUANTIZE}, {"QuantizeLinear", QNN_OP_QUANTIZE}, diff --git a/onnxruntime/core/providers/qnn/builder/opbuilder/resize_op_builder.cc b/onnxruntime/core/providers/qnn/builder/opbuilder/resize_op_builder.cc index f36854cfea..511f2a5149 100644 --- a/onnxruntime/core/providers/qnn/builder/opbuilder/resize_op_builder.cc +++ b/onnxruntime/core/providers/qnn/builder/opbuilder/resize_op_builder.cc @@ -14,6 +14,7 @@ #include "core/common/safeint.h" #include "core/providers/qnn/builder/opbuilder/base_op_builder.h" +#include "core/providers/qnn/builder/qnn_utils.h" namespace onnxruntime { namespace qnn { @@ -157,19 +158,6 @@ Status ResizeOpBuilder::GetQnnModeFromString(const std::array -static bool ArrayHasString(const std::array& strings, std::string_view str) { - for (auto s : strings) { - if (s == str) { - return true; - } - } - - return false; -} - // Resize ops are sensitive with data layout, no special validation so far // The nodes from 1st call of GetCapability do not get layout transformer applied, it's still NCHW // The nodes from 2nd call of GetCapability get layout transformer applied, it's NHWC @@ -252,6 +240,7 @@ Status ResizeOpBuilder::ValidateOp(QnnModelWrapper& qnn_model_wrapper, const Nod Status ResizeOpBuilder::ValidateQDQOp(QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit) const { NodeAttrHelper node_helper(node_unit); + using namespace onnxruntime::qnn::utils; // Check mode const std::string interp_mode = GetOnnxAttr(node_helper, onnx_mode_attr); ORT_RETURN_IF_NOT(ArrayHasString(supported_modes, interp_mode), "QNN EP: Resize does not support mode ", diff --git a/onnxruntime/core/providers/qnn/builder/opbuilder/simple_op_builder.cc b/onnxruntime/core/providers/qnn/builder/opbuilder/simple_op_builder.cc index 8d9a79ddf8..ca18c051a9 100644 --- a/onnxruntime/core/providers/qnn/builder/opbuilder/simple_op_builder.cc +++ b/onnxruntime/core/providers/qnn/builder/opbuilder/simple_op_builder.cc @@ -30,18 +30,9 @@ class SimpleOpBuilder : public BaseOpBuilder { private: Status ExplictOpCheck(const QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit) const; - Status ProcessAlphaAttribute(QnnModelWrapper& qnn_model_wrapper, - const NodeUnit& node_unit, - std::vector& param_tensor_names) const; - Status ProcessAlphaAttributeAsInput(QnnModelWrapper& qnn_model_wrapper, - const NodeUnit& node_unit, - const std::string input_name) const; - Status ProcessBlockSizeAttribute(QnnModelWrapper& qnn_model_wrapper, - const NodeUnit& node_unit, - std::vector& param_tensor_names) const; - Status ProcessModeAttribute(QnnModelWrapper& qnn_model_wrapper, - const NodeUnit& node_unit, - std::vector& param_tensor_names) const; + + static constexpr std::array gridsample_supported_modes = {"bilinear", "nearest"}; + static constexpr std::array gridsample_supported_padding_modes = {"zeros", "border", "reflection"}; }; Status SimpleOpBuilder::ExplictOpCheck(const QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit) const { @@ -57,12 +48,22 @@ Status SimpleOpBuilder::ExplictOpCheck(const QnnModelWrapper& qnn_model_wrapper, "QNN Softmax only supports an `axis` attribute equal to input_rank-1 (or -1)"); } + if (node_unit.OpType() == "GridSample") { + NodeAttrHelper node_helper(node_unit); + std::string mode = node_helper.Get("mode", "linear"); + ORT_RETURN_IF_NOT(utils::ArrayHasString(gridsample_supported_modes, mode), "GridSample does not support mode ", + mode.c_str()); + std::string padding_mode = node_helper.Get("padding_mode", "zeros"); + ORT_RETURN_IF_NOT(utils::ArrayHasString(gridsample_supported_padding_modes, padding_mode), "GridSample does not support padding_mode ", + padding_mode.c_str()); + } + return Status::OK(); } -Status SimpleOpBuilder::ProcessAlphaAttribute(QnnModelWrapper& qnn_model_wrapper, - const NodeUnit& node_unit, - std::vector& param_tensor_names) const { +Status ProcessAlphaAttribute(QnnModelWrapper& qnn_model_wrapper, + const NodeUnit& node_unit, + std::vector& param_tensor_names) { NodeAttrHelper node_helper(node_unit); float alpha = node_helper.Get("alpha", 1.0f); Qnn_Scalar_t alpha_qnn_scalar = QNN_SCALAR_INIT; @@ -76,9 +77,9 @@ Status SimpleOpBuilder::ProcessAlphaAttribute(QnnModelWrapper& qnn_model_wrapper return Status::OK(); } -Status SimpleOpBuilder::ProcessBlockSizeAttribute(QnnModelWrapper& qnn_model_wrapper, - const NodeUnit& node_unit, - std::vector& param_tensor_names) const { +Status ProcessBlockSizeAttribute(QnnModelWrapper& qnn_model_wrapper, + const NodeUnit& node_unit, + std::vector& param_tensor_names) { NodeAttrHelper node_helper(node_unit); uint32_t block_size = node_helper.Get("blocksize", static_cast(0)); std::vector block_size_shape{2}; @@ -91,9 +92,9 @@ Status SimpleOpBuilder::ProcessBlockSizeAttribute(QnnModelWrapper& qnn_model_wra return Status::OK(); } -Status SimpleOpBuilder::ProcessModeAttribute(QnnModelWrapper& qnn_model_wrapper, - const NodeUnit& node_unit, - std::vector& param_tensor_names) const { +Status ProcessModeAttribute(QnnModelWrapper& qnn_model_wrapper, + const NodeUnit& node_unit, + std::vector& param_tensor_names) { NodeAttrHelper node_helper(node_unit); std::string mode = node_helper.Get("mode", "DCR"); Qnn_Scalar_t mode_qnn_scalar = QNN_SCALAR_INIT; @@ -114,9 +115,9 @@ Status SimpleOpBuilder::ProcessModeAttribute(QnnModelWrapper& qnn_model_wrapper, } // Process alpha attribute as input for Qnn LeakyRelu -Status SimpleOpBuilder::ProcessAlphaAttributeAsInput(QnnModelWrapper& qnn_model_wrapper, - const NodeUnit& node_unit, - const std::string input_name) const { +Status ProcessAlphaAttributeAsInput(QnnModelWrapper& qnn_model_wrapper, + const NodeUnit& node_unit, + const std::string input_name) { NodeAttrHelper node_helper(node_unit); Qnn_QuantizeParams_t quantize_param = QNN_QUANTIZE_PARAMS_INIT; Qnn_DataType_t qnn_data_type = QNN_DATATYPE_FLOAT_32; @@ -149,6 +150,51 @@ Status SimpleOpBuilder::ProcessAlphaAttributeAsInput(QnnModelWrapper& qnn_model_ return Status::OK(); } +Status ProcessGridSampleAttributes(QnnModelWrapper& qnn_model_wrapper, + const NodeUnit& node_unit, + std::vector& param_tensor_names) { + NodeAttrHelper node_helper(node_unit); + int64_t align_corners = node_helper.Get("align_corners", static_cast(0)); + Qnn_Scalar_t align_corners_qnn_scalar = QNN_SCALAR_INIT; + align_corners_qnn_scalar.dataType = QNN_DATATYPE_BOOL_8; + align_corners_qnn_scalar.bool8Value = static_cast(align_corners == 0 ? 0 : 1); + QnnParamWrapper align_corners_param(node_unit.Index(), node_unit.Name(), QNN_OP_GRID_SAMPLE_PARAM_ALIGN_CORNERS, align_corners_qnn_scalar); + param_tensor_names.push_back(align_corners_param.GetParamTensorName()); + qnn_model_wrapper.AddParamWrapper(std::move(align_corners_param)); + + std::string mode = node_helper.Get("mode", "linear"); + Qnn_Scalar_t mode_qnn_scalar = QNN_SCALAR_INIT; + mode_qnn_scalar.dataType = QNN_DATATYPE_UINT_32; + if ("bilinear" == mode) { + mode_qnn_scalar.uint32Value = QNN_OP_GRID_SAMPLE_MODE_BILINEAR; + } else if ("nearest" == mode) { + mode_qnn_scalar.uint32Value = QNN_OP_GRID_SAMPLE_MODE_NEAREST; + } else { + return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "GridSample mode only support bilinear & nearest."); + } + QnnParamWrapper mode_param(node_unit.Index(), node_unit.Name(), QNN_OP_GRID_SAMPLE_PARAM_MODE, mode_qnn_scalar); + param_tensor_names.push_back(mode_param.GetParamTensorName()); + qnn_model_wrapper.AddParamWrapper(std::move(mode_param)); + + std::string padding_mode = node_helper.Get("padding_mode", "zeros"); + Qnn_Scalar_t padding_mode_qnn_scalar = QNN_SCALAR_INIT; + padding_mode_qnn_scalar.dataType = QNN_DATATYPE_UINT_32; + if ("zeros" == padding_mode) { + padding_mode_qnn_scalar.uint32Value = QNN_OP_GRID_SAMPLE_PADDING_MODE_ZEROS; + } else if ("border" == padding_mode) { + padding_mode_qnn_scalar.uint32Value = QNN_OP_GRID_SAMPLE_PADDING_MODE_BORDER; + } else if ("reflection" == padding_mode) { + padding_mode_qnn_scalar.uint32Value = QNN_OP_GRID_SAMPLE_PADDING_MODE_REFLECTION; + } else { + return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "GridSample padding_mode only support zeros, border & reflection."); + } + QnnParamWrapper padding_mode_param(node_unit.Index(), node_unit.Name(), QNN_OP_GRID_SAMPLE_PARAM_PADDING_MODE, padding_mode_qnn_scalar); + param_tensor_names.push_back(padding_mode_param.GetParamTensorName()); + qnn_model_wrapper.AddParamWrapper(std::move(padding_mode_param)); + + return Status::OK(); +} + Status SimpleOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit, std::vector&& input_names, @@ -163,7 +209,7 @@ Status SimpleOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_w if (do_op_validation) { ORT_RETURN_IF_ERROR(ExplictOpCheck(qnn_model_wrapper, node_unit)); // Skip the op validation for DepthToSpace & SpaceToDepth if it's not NHWC data layout - if (node_unit.Domain() != kMSInternalNHWCDomain && (op_type == "DepthToSpace" || op_type == "SpaceToDepth")) { + if (node_unit.Domain() != kMSInternalNHWCDomain && (op_type == "DepthToSpace" || op_type == "SpaceToDepth" || op_type == "GridSample")) { return Status::OK(); } } @@ -211,6 +257,10 @@ Status SimpleOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_w ORT_RETURN_IF_ERROR(ProcessBlockSizeAttribute(qnn_model_wrapper, node_unit, param_tensor_names)); } + if (op_type == "GridSample") { + ORT_RETURN_IF_ERROR(ProcessGridSampleAttributes(qnn_model_wrapper, node_unit, param_tensor_names)); + } + ORT_RETURN_IF_ERROR(ProcessOutputs(qnn_model_wrapper, node_unit, std::move(input_names), std::move(param_tensor_names), diff --git a/onnxruntime/core/providers/qnn/builder/qnn_utils.h b/onnxruntime/core/providers/qnn/builder/qnn_utils.h index 1c4d85a0d1..a54e0c8276 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_utils.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_utils.h @@ -35,6 +35,19 @@ inline void InitializeQuantizeParam(Qnn_QuantizeParams_t& quantize_param, bool i quantize_param.scaleOffsetEncoding.offset = offset; } +// Utility function that checks if an array of strings contains a specific string. +// Used to validate ONNX operator attributes. +template +static bool ArrayHasString(const std::array& strings, std::string_view str) { + for (auto s : strings) { + if (s == str) { + return true; + } + } + + return false; +} + } // namespace utils } // namespace qnn } // namespace onnxruntime diff --git a/onnxruntime/test/contrib_ops/gridsample_test.cc b/onnxruntime/test/contrib_ops/gridsample_test.cc index 8d77978532..1f31c2bd21 100644 --- a/onnxruntime/test/contrib_ops/gridsample_test.cc +++ b/onnxruntime/test/contrib_ops/gridsample_test.cc @@ -71,7 +71,7 @@ TEST(GridsampleContribOpTest, gridsample_paddingmode_reflection) { 5.0000f, 5.0000f, 10.0000f, 10.0000f}); test.AddAttribute("padding_mode", "reflection"); test.AddOutput("Y", {1, 1, 2, 4}, {2.5000f, 0.0000f, 1.7000f, 2.5000f, 2.5000f, 1.7000f, 5.0000f, 2.5000f}); - test.Run(); + test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kQnnExecutionProvider}); // Accuracy issue for QNN } TEST(GridsampleContribOpTest, gridsample_aligncorners_true) { diff --git a/onnxruntime/test/onnx/main.cc b/onnxruntime/test/onnx/main.cc index ab19a8d2b6..8a6f3b1cd8 100644 --- a/onnxruntime/test/onnx/main.cc +++ b/onnxruntime/test/onnx/main.cc @@ -1222,6 +1222,7 @@ select from 'TF8', 'TF16', 'UINT8', 'FLOAT', 'ITENSOR'. \n)"); broken_tests.insert({"sce_sum_expanded", "result differs"}); broken_tests.insert({"sce_sum_log_prob", "result differs"}); broken_tests.insert({"sce_sum_log_prob_expanded", "result differs"}); + broken_tests.insert({"gridsample_reflection_padding", "result differs"}); } #if defined(_WIN32) && !defined(_WIN64) broken_tests.insert({"vgg19", "failed: bad allocation"}); diff --git a/onnxruntime/test/providers/qnn/simple_op_htp_test.cc b/onnxruntime/test/providers/qnn/simple_op_htp_test.cc index c87ff3b224..a6ef0be16c 100644 --- a/onnxruntime/test/providers/qnn/simple_op_htp_test.cc +++ b/onnxruntime/test/providers/qnn/simple_op_htp_test.cc @@ -96,24 +96,40 @@ static void RunQDQUnaryOpTest(const TestInputDef& input_def, const std::s 1e-5f); } -template -static GetTestModelFn BuildBinaryOpTestCase(const std::string& op_type, const TestInputDef& input0_def, - const TestInputDef& input1_def) { - return [op_type, input0_def, input1_def](ModelTestBuilder& builder) { +// TODO: share with other op tests +// Creates the graph with two inputs and attributes +template +static GetTestModelFn BuildOpTestCase(const std::string& op_type, + const TestInputDef& input0_def, + const TestInputDef& input1_def, + const std::vector& attrs) { + return [op_type, input0_def, input1_def, attrs](ModelTestBuilder& builder) { NodeArg* input0 = MakeTestInput(builder, input0_def); NodeArg* input1 = MakeTestInput(builder, input1_def); auto* output = builder.MakeOutput(); - builder.AddNode(op_type, {input0, input1}, {output}); + Node& onnx_node = builder.AddNode(op_type, {input0, input1}, {output}); + + for (const auto& attr : attrs) { + onnx_node.AddAttributeProto(attr); + } }; } -template -static GetTestQDQModelFn BuildQDQBinaryOpTestCase(const std::string& op_type, - const TestInputDef& input0_def, - const TestInputDef& input1_def) { - return [op_type, input0_def, input1_def](ModelTestBuilder& builder, - std::vector>& output_qparams) { +// Creates the graph with two inputs and attributes +// _______________________ +// | | +// input0_u8 -> DQ -> | SimpleOp | -> Q -> output_u8 +// input1_u8 -> DQ -> |_______________________| +// +// Currently used to test QNN EP. +template +static GetTestQDQModelFn BuildQDQOpTestCase(const std::string& op_type, + const TestInputDef& input0_def, + const TestInputDef& input1_def, + const std::vector& attrs) { + return [op_type, input0_def, input1_def, attrs](ModelTestBuilder& builder, + std::vector>& output_qparams) { NodeArg* input0 = MakeTestInput(builder, input0_def); NodeArg* input1 = MakeTestInput(builder, input1_def); @@ -126,7 +142,11 @@ static GetTestQDQModelFn BuildQDQBinaryOpTestCase(const std::string& // Op -> op_output auto* op_output = builder.MakeIntermediate(); - builder.AddNode(op_type, {qdq0_output, qdq1_output}, {op_output}); + Node& onnx_node = builder.AddNode(op_type, {qdq0_output, qdq1_output}, {op_output}); + + for (const auto& attr : attrs) { + onnx_node.AddAttributeProto(attr); + } // op_output -> Q -> DQ -> output AddQDQNodePairWithOutputAsGraphOutput(builder, op_output, output_qparams[0].scale, @@ -135,9 +155,12 @@ static GetTestQDQModelFn BuildQDQBinaryOpTestCase(const std::string& } template -static void RunQDQBinaryOpTest(const std::string& op_type, const TestInputDef& input0_def, - const TestInputDef& input1_def, int opset_version, - ExpectedEPNodeAssignment expected_ep_assignment) { +static void RunQDQOpTest(const std::string& op_type, + const TestInputDef& input0_def, + const TestInputDef& input1_def, + const std::vector& attrs, + int opset_version, + ExpectedEPNodeAssignment expected_ep_assignment) { ProviderOptions provider_options; #if defined(_WIN32) provider_options["backend_path"] = "QnnHtp.dll"; @@ -145,8 +168,8 @@ static void RunQDQBinaryOpTest(const std::string& op_type, const TestInputDef(op_type, input0_def, input1_def), - BuildQDQBinaryOpTestCase(op_type, input0_def, input1_def), + TestQDQModelAccuracy(BuildOpTestCase(op_type, input0_def, input1_def, attrs), + BuildQDQOpTestCase(op_type, input0_def, input1_def, attrs), provider_options, opset_version, expected_ep_assignment, @@ -154,9 +177,12 @@ static void RunQDQBinaryOpTest(const std::string& op_type, const TestInputDef -static void RunBinaryOpTest(const std::string& op_type, const TestInputDef& input0_def, - const TestInputDef& input1_def, int opset_version, - ExpectedEPNodeAssignment expected_ep_assignment) { +static void RunOpTest(const std::string& op_type, + const TestInputDef& input0_def, + const TestInputDef& input1_def, + const std::vector& attrs, + int opset_version, + ExpectedEPNodeAssignment expected_ep_assignment) { ProviderOptions provider_options; #if defined(_WIN32) provider_options["backend_path"] = "QnnHtp.dll"; @@ -165,7 +191,7 @@ static void RunBinaryOpTest(const std::string& op_type, const TestInputDef(op_type, input0_def, input1_def), + RunQnnModelTest(BuildOpTestCase(op_type, input0_def, input1_def, attrs), provider_options, opset_version, expected_ep_assignment); @@ -427,35 +453,49 @@ TEST_F(QnnHTPBackendTests, QuantAccuracyTest) { // Test QDQ Add TEST_F(QnnHTPBackendTests, BinaryOp_Add4D) { - RunQDQBinaryOpTest("Add", TestInputDef({1, 2, 2, 2}, false, -10.0f, 10.0f), - TestInputDef({1, 2, 2, 2}, false, -10.0f, 10.0f), - 17, ExpectedEPNodeAssignment::All); + RunQDQOpTest("Add", + TestInputDef({1, 2, 2, 2}, false, -10.0f, 10.0f), + TestInputDef({1, 2, 2, 2}, false, -10.0f, 10.0f), + {}, + 17, + ExpectedEPNodeAssignment::All); } // Test QDQ Sub TEST_F(QnnHTPBackendTests, BinaryOp_Sub4D) { - RunQDQBinaryOpTest("Sub", TestInputDef({1, 3, 8, 8}, false, -10.0f, 10.0f), - TestInputDef({1, 3, 8, 8}, false, -10.0f, 10.0f), - 17, ExpectedEPNodeAssignment::All); + RunQDQOpTest("Sub", + TestInputDef({1, 3, 8, 8}, false, -10.0f, 10.0f), + TestInputDef({1, 3, 8, 8}, false, -10.0f, 10.0f), + {}, + 17, + ExpectedEPNodeAssignment::All); } TEST_F(QnnHTPBackendTests, BinaryOp_Sub4D_LargeInputs) { - RunQDQBinaryOpTest("Sub", TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), - TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), - 17, ExpectedEPNodeAssignment::All); + RunQDQOpTest("Sub", + TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), + TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), + {}, + 17, + ExpectedEPNodeAssignment::All); } TEST_F(QnnHTPBackendTests, BinaryOp_Sub4D_Broadcast) { - RunQDQBinaryOpTest("Sub", TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), - TestInputDef({3, 1, 1}, true, {1.0f, 0.5f, -0.3f}), - 17, ExpectedEPNodeAssignment::All); + RunQDQOpTest("Sub", + TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), + TestInputDef({3, 1, 1}, true, {1.0f, 0.5f, -0.3f}), + {}, + 17, + ExpectedEPNodeAssignment::All); } TEST_F(QnnHTPBackendTests, BinaryOp_Div4D_SmallInputs) { - RunQDQBinaryOpTest("Div", - TestInputDef({1, 2, 2, 2}, false, {-10.0f, -8.0f, -1.0f, 0.0f, 1.0f, 2.1f, 8.0f, 10.0f}), - TestInputDef({1, 2, 2, 2}, false, {5.0f, 4.0f, 1.0f, 1.0f, 1.0f, 4.0f, 4.0f, 5.0f}), - 17, ExpectedEPNodeAssignment::All); + RunQDQOpTest("Div", + TestInputDef({1, 2, 2, 2}, false, {-10.0f, -8.0f, -1.0f, 0.0f, 1.0f, 2.1f, 8.0f, 10.0f}), + TestInputDef({1, 2, 2, 2}, false, {5.0f, 4.0f, 1.0f, 1.0f, 1.0f, 4.0f, 4.0f, 5.0f}), + {}, + 17, + ExpectedEPNodeAssignment::All); } // TODO: Enable when this is fixed. @@ -465,36 +505,116 @@ TEST_F(QnnHTPBackendTests, BinaryOp_Div4D_SmallInputs) { // QNN QDQ val: 0 (err 277957.3125) // CPU QDQ val: -516716.71875 (err 238759.40625) TEST_F(QnnHTPBackendTests, DISABLED_BinaryOp_Div4D_LargeInputs) { - RunQDQBinaryOpTest("Div", TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), - TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), - 17, ExpectedEPNodeAssignment::All); + RunQDQOpTest("Div", + TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), + TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), + {}, + 17, + ExpectedEPNodeAssignment::All); } TEST_F(QnnHTPBackendTests, BinaryOp_Div4D_Broadcast) { - RunQDQBinaryOpTest("Div", TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), - TestInputDef({3, 1, 1}, true, {1.0f, 0.5f, -0.3f}), - 17, ExpectedEPNodeAssignment::All); + RunQDQOpTest("Div", + TestInputDef({1, 3, 768, 1152}, false, -1.0f, 1.0f), + TestInputDef({3, 1, 1}, true, {1.0f, 0.5f, -0.3f}), + {}, + 17, + ExpectedEPNodeAssignment::All); } // Test QDQ Mul TEST_F(QnnHTPBackendTests, BinaryOp_Mul4D) { - RunQDQBinaryOpTest("Mul", TestInputDef({1, 2, 2, 2}, false, -10.0f, 10.0f), - TestInputDef({1, 2, 2, 2}, false, -10.0f, 10.0f), - 17, ExpectedEPNodeAssignment::All); + RunQDQOpTest("Mul", + TestInputDef({1, 2, 2, 2}, false, -10.0f, 10.0f), + TestInputDef({1, 2, 2, 2}, false, -10.0f, 10.0f), + {}, + 17, + ExpectedEPNodeAssignment::All); } -// Test QDQ And -TEST_F(QnnHTPBackendTests, BinaryOp_And4D) { - RunBinaryOpTest("And", TestInputDef({1, 4}, false, {false, false, true, true}), - TestInputDef({1, 4}, false, {false, true, false, true}), - 17, ExpectedEPNodeAssignment::All); +// Test And +TEST_F(QnnCPUBackendTests, BinaryOp_And4D) { + RunOpTest("And", + TestInputDef({1, 4}, false, {false, false, true, true}), + TestInputDef({1, 4}, false, {false, true, false, true}), + {}, + 17, + ExpectedEPNodeAssignment::All); } -// Test that Or is not yet supported on HTP backend. -TEST_F(QnnHTPBackendTests, BinaryOp_HTP_Or_Unsupported) { - RunBinaryOpTest("Or", TestInputDef({1, 4}, false, {false, false, true, true}), - TestInputDef({1, 4}, false, {false, true, false, true}), - 17, ExpectedEPNodeAssignment::None); +// Test that Or is not yet supported on CPU backend. +TEST_F(QnnCPUBackendTests, BinaryOp_HTP_Or_Unsupported) { + RunOpTest("Or", + TestInputDef({1, 4}, false, {false, false, true, true}), + TestInputDef({1, 4}, false, {false, true, false, true}), + {}, + 17, + ExpectedEPNodeAssignment::None); +} + +// Test QDQ GridSample with bilinear +TEST_F(QnnHTPBackendTests, GridSample_Bilinear) { + RunQDQOpTest("GridSample", + TestInputDef({1, 1, 3, 2}, false, -10.0f, 10.0f), + TestInputDef({1, 2, 4, 2}, false, -10.0f, 10.0f), + {utils::MakeAttribute("align_corners", static_cast(0)), + utils::MakeAttribute("mode", "bilinear"), + utils::MakeAttribute("padding_mode", "zeros")}, + 17, + ExpectedEPNodeAssignment::All); +} + +// Test QDQ GridSample with align corners +TEST_F(QnnHTPBackendTests, GridSample_AlignCorners) { + RunQDQOpTest("GridSample", + TestInputDef({1, 1, 3, 2}, false, -10.0f, 10.0f), + TestInputDef({1, 2, 4, 2}, false, -10.0f, 10.0f), + {utils::MakeAttribute("align_corners", static_cast(1)), + utils::MakeAttribute("mode", "bilinear"), + utils::MakeAttribute("padding_mode", "zeros")}, + 17, + ExpectedEPNodeAssignment::All); +} + +// Test QDQ GridSample with padding mode: border +// Inaccuracy detected for output 'output', element 0. +// Output quant params: scale=0.046370312571525574, zero_point=129. +// Expected val: 3.3620510101318359 +// QNN QDQ val: 3.2922921180725098 (err 0.069758892059326172) +// CPU QDQ val: 3.3850328922271729 (err 0.022981882095336914) +TEST_F(QnnHTPBackendTests, DISABLED_GridSample_BorderPadding) { + RunQDQOpTest("GridSample", + TestInputDef({1, 1, 3, 2}, false, -10.0f, 10.0f), + TestInputDef({1, 2, 4, 2}, false, -10.0f, 10.0f), + {utils::MakeAttribute("mode", "bilinear"), + utils::MakeAttribute("padding_mode", "border")}, + 17, + ExpectedEPNodeAssignment::All); +} + +// Test QDQ GridSample with nearest mode +TEST_F(QnnHTPBackendTests, GridSample_Nearest) { + RunQDQOpTest("GridSample", + TestInputDef({1, 1, 3, 2}, false, -10.0f, 10.0f), + TestInputDef({1, 2, 4, 2}, false, -10.0f, 10.0f), + {utils::MakeAttribute("mode", "nearest")}, + 17, + ExpectedEPNodeAssignment::All); +} + +// Test QDQ GridSample with reflection padding mode +// Inaccuracy detected for output 'output', element 2. +// Output quant params: scale=0.024269860237836838, zero_point=0. +// Expected val: 3.212885856628418 +// QNN QDQ val: 3.1308119297027588 (err 0.08207392692565918) +// CPU QDQ val: 3.2036216259002686 (err 0.0092642307281494141) +TEST_F(QnnHTPBackendTests, DISABLED_GridSample_ReflectionPaddingMode) { + RunQDQOpTest("GridSample", + TestInputDef({1, 1, 3, 2}, false, -10.0f, 10.0f), + TestInputDef({1, 2, 4, 2}, false, -10.0f, 10.0f), + {utils::MakeAttribute("padding_mode", "reflection")}, + 17, + ExpectedEPNodeAssignment::All); } #endif // defined(__aarch64__) || defined(_M_ARM64) || defined(__linux__)