diff --git a/onnxruntime/core/providers/webnn/builders/helper.h b/onnxruntime/core/providers/webnn/builders/helper.h index 518022fc0f..e507d32129 100644 --- a/onnxruntime/core/providers/webnn/builders/helper.h +++ b/onnxruntime/core/providers/webnn/builders/helper.h @@ -153,6 +153,7 @@ static const InlinedHashMap op_map = { {"Gemm", "gemm"}, {"GlobalAveragePool", "averagePool2d"}, {"GlobalMaxPool", "maxPool2d"}, + {"GlobalLpPool", "l2Pool2d"}, {"Greater", "greater"}, {"GroupNormalization", "meanVarianceNormalization"}, {"HardSigmoid", "hardSigmoid"}, @@ -162,6 +163,8 @@ static const InlinedHashMap op_map = { {"LayerNormalization", "meanVarianceNormalization"}, {"LeakyRelu", "leakyRelu"}, {"Less", "lesser"}, + {"Log", "log"}, + {"LpPool", "l2Pool2d"}, {"MatMul", "matmul"}, {"Max", "max"}, {"MaxPool", "maxPool2d"}, diff --git a/onnxruntime/core/providers/webnn/builders/impl/base_op_builder.cc b/onnxruntime/core/providers/webnn/builders/impl/base_op_builder.cc index a44562edc7..a893d2ff2c 100644 --- a/onnxruntime/core/providers/webnn/builders/impl/base_op_builder.cc +++ b/onnxruntime/core/providers/webnn/builders/impl/base_op_builder.cc @@ -100,8 +100,9 @@ bool BaseOpBuilder::HasSupportedOpSet(const Node& node, const logging::Logger& logger) const { auto since_version = node.SinceVersion(); if (since_version < GetMinSupportedOpSet(node) || since_version > GetMaxSupportedOpSet(node)) { - LOGS(logger, VERBOSE) << "Current opset is " << since_version << ", " - << node.OpType() << " is only supported for opset [" + LOGS(logger, VERBOSE) << "Current opset since version of " + << node.OpType() << " is " << since_version + << ", WebNN EP only supports for its opset [" << GetMinSupportedOpSet(node) << ", " << GetMaxSupportedOpSet(node) << "]"; return false; diff --git a/onnxruntime/core/providers/webnn/builders/impl/base_op_builder.h b/onnxruntime/core/providers/webnn/builders/impl/base_op_builder.h index 601db0c1fc..301927d9c6 100644 --- a/onnxruntime/core/providers/webnn/builders/impl/base_op_builder.h +++ b/onnxruntime/core/providers/webnn/builders/impl/base_op_builder.h @@ -41,7 +41,11 @@ class BaseOpBuilder : public IOpBuilder { // ONNX Runtime only *guarantees* support for models stamped // with opset version 7 or above for opset domain 'ai.onnx'. - virtual int GetMinSupportedOpSet(const Node& /* node */) const { return 7; } + // WebNN EP ignores node support for opset less than 7 by + // default as which will be fallback earlier by ONNX Runtime. + // We still set the mininal supported opset to 1 as we couldn't + // get the model opset version at this stage. + virtual int GetMinSupportedOpSet(const Node& /* node */) const { return 1; } virtual int GetMaxSupportedOpSet(const Node& /* node */) const { return 19; } private: diff --git a/onnxruntime/core/providers/webnn/builders/impl/pool_op_builder.cc b/onnxruntime/core/providers/webnn/builders/impl/pool_op_builder.cc index 32c619ea7a..240b6e0d48 100644 --- a/onnxruntime/core/providers/webnn/builders/impl/pool_op_builder.cc +++ b/onnxruntime/core/providers/webnn/builders/impl/pool_op_builder.cc @@ -38,15 +38,22 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, emscripten::val input = model_builder.GetOperand(input_defs[0]->Name()); bool is_global_pooling = false; - bool is_average_pool = false; + std::string webnn_op_name; if (op_type == "GlobalAveragePool") { is_global_pooling = true; - is_average_pool = true; + webnn_op_name = "averagePool2d"; } else if (op_type == "GlobalMaxPool") { is_global_pooling = true; + webnn_op_name = "maxPool2d"; + } else if (op_type == "GlobalLpPool") { + is_global_pooling = true; + webnn_op_name = "l2Pool2d"; } else if (op_type == "AveragePool") { - is_average_pool = true; + webnn_op_name = "averagePool2d"; } else if (op_type == "MaxPool") { + webnn_op_name = "maxPool2d"; + } else if (op_type == "LpPool") { + webnn_op_name = "l2Pool2d"; } else { return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "PoolOpBuilder, unknown op: ", op_type); } @@ -97,12 +104,7 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, options.set("roundingType", ceil_mode == 0 ? emscripten::val("floor") : emscripten::val("ceil")); - emscripten::val output = emscripten::val::object(); - if (is_average_pool) { - output = model_builder.GetBuilder().call("averagePool2d", input, options); - } else { - output = model_builder.GetBuilder().call("maxPool2d", input, options); - } + emscripten::val output = model_builder.GetBuilder().call(webnn_op_name.c_str(), input, options); model_builder.AddOperand(node.OutputDefs()[0]->Name(), std::move(output)); return Status::OK(); } @@ -127,21 +129,36 @@ bool PoolOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializer return false; } - if (op_type == "AveragePool" || op_type == "MaxPool") { - NodeAttrHelper helper(node); - const auto storage_order = helper.Get("storage_order", 0); - if (storage_order == 1) { - LOGS(logger, VERBOSE) << "storage_order == 1 is not supported"; - return false; - } - + NodeAttrHelper helper(node); + if (op_type == "AveragePool" || op_type == "LpPool" || op_type == "MaxPool") { if (helper.Get("kernel_shape", std::vector{1, 1}).size() != 2) { LOGS(logger, VERBOSE) << "Only pooling 2d is supported"; return false; } + } + if (op_type == "AveragePool") { + if (helper.Get("count_include_pad", 0) != 0) { + LOGS(logger, VERBOSE) << "AveragePool only supports count_include_pad == 0"; + return false; + } + } + + if (op_type == "MaxPool") { + if (helper.Get("storage_order", 0) == 1) { + LOGS(logger, VERBOSE) << "MaxPool storage_order == 1 is not supported"; + return false; + } if (node.OutputDefs().size() != 1) { - LOGS(logger, VERBOSE) << "Argmax in maxpooling is not supported"; + LOGS(logger, VERBOSE) << "MaxPool only supports one output"; + return false; + } + } + + if (op_type == "GlobalLpPool" || op_type == "LpPool") { + // WebNN only supports l2Pool2d. + if (helper.Get("p", 2) != 2) { + LOGS(logger, VERBOSE) << op_type << " only supports p == 2"; return false; } } @@ -155,9 +172,11 @@ void CreatePoolOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_ static std::vector op_types = { + "AveragePool", "GlobalAveragePool", "GlobalMaxPool", - "AveragePool", + "GlobalLpPool", + "LpPool", "MaxPool", }; diff --git a/onnxruntime/core/providers/webnn/builders/impl/unary_op_builder.cc b/onnxruntime/core/providers/webnn/builders/impl/unary_op_builder.cc index 1b00c720ed..e6c5cf2408 100644 --- a/onnxruntime/core/providers/webnn/builders/impl/unary_op_builder.cc +++ b/onnxruntime/core/providers/webnn/builders/impl/unary_op_builder.cc @@ -43,6 +43,8 @@ Status UnaryOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const output = model_builder.GetBuilder().call("floor", input); } else if (op_type == "Identity") { output = model_builder.GetBuilder().call("identity", input); + } else if (op_type == "Log") { + output = model_builder.GetBuilder().call("log", input); } else if (op_type == "Neg") { output = model_builder.GetBuilder().call("neg", input); } else if (op_type == "Not") { @@ -77,6 +79,7 @@ void CreateUnaryOpBuilder(const std::string& op_type, OpBuilderRegistrations& op "Exp", "Floor", "Identity", + "Log", "Neg", "Not", "Reciprocal", diff --git a/onnxruntime/core/providers/webnn/builders/op_builder_factory.cc b/onnxruntime/core/providers/webnn/builders/op_builder_factory.cc index 863cefda70..5d1761a732 100644 --- a/onnxruntime/core/providers/webnn/builders/op_builder_factory.cc +++ b/onnxruntime/core/providers/webnn/builders/op_builder_factory.cc @@ -23,6 +23,7 @@ static OpBuilderRegistrations CreateOpBuilderRegistrations() { CreateUnaryOpBuilder("Exp", op_registrations); CreateUnaryOpBuilder("Floor", op_registrations); CreateUnaryOpBuilder("Identity", op_registrations); + CreateUnaryOpBuilder("Log", op_registrations); CreateUnaryOpBuilder("Neg", op_registrations); CreateUnaryOpBuilder("Not", op_registrations); CreateUnaryOpBuilder("Reciprocal", op_registrations); @@ -117,9 +118,11 @@ static OpBuilderRegistrations CreateOpBuilderRegistrations() { } { // Pool + CreatePoolOpBuilder("AveragePool", op_registrations); CreatePoolOpBuilder("GlobalAveragePool", op_registrations); CreatePoolOpBuilder("GlobalMaxPool", op_registrations); - CreatePoolOpBuilder("AveragePool", op_registrations); + CreatePoolOpBuilder("GlobalLpPool", op_registrations); + CreatePoolOpBuilder("LpPool", op_registrations); CreatePoolOpBuilder("MaxPool", op_registrations); }