From eee8a28b3a1eb56f7524fff4cc37cccb74972cab Mon Sep 17 00:00:00 2001 From: Ningxin Hu Date: Mon, 12 Jul 2021 17:46:28 +0800 Subject: [PATCH] Implement op_webnn --- CMakeLists.txt | 9 + cmake/templates/cvconfig.h.in | 3 + modules/dnn/CMakeLists.txt | 10 +- modules/dnn/include/opencv2/dnn/dnn.hpp | 5 +- modules/dnn/src/dnn.cpp | 268 +++++++++++++++++++++++- modules/dnn/src/op_webnn.cpp | 146 ++++++++----- modules/dnn/src/op_webnn.hpp | 61 ++++-- modules/dnn/test/test_common.hpp | 3 +- modules/dnn/test/test_common.impl.hpp | 15 +- 9 files changed, 430 insertions(+), 90 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd12a094884..f049e1a9e5dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1608,6 +1608,15 @@ if(WITH_VULKAN OR HAVE_VULKAN) endif() endif() +if(WITH_WEBNN OR HAVE_WEBNN) + status("") + status(" WebNN:" HAVE_WEBNN THEN "YES" ELSE "NO") + if(HAVE_WEBNN) + status(" Include path:" WEBNN_HEADER_DIRS THEN "${WEBNN_HEADER_DIRS}" ELSE "NO") + status(" Link libraries:" WEBNN_LIBRARIES THEN "${WEBNN_LIBRARIES}" ELSE "NO") + endif() +endif() + if(WITH_OPENCL OR HAVE_OPENCL) ocv_build_features_string(opencl_features IF HAVE_OPENCL_SVM THEN "SVM" diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index e79e1ec0a1bc..99ec4802d2cb 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -59,6 +59,9 @@ /* Vulkan support */ #cmakedefine HAVE_VULKAN +/* Webnn support */ +#cmakedefine HAVE_WEBNN + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H 1 diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index 4c8129cbda1c..3ae87ef72edd 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -133,6 +133,14 @@ if(HAVE_TENGINE) list(APPEND libs -Wl,--whole-archive ${TENGINE_LIBRARIES} -Wl,--no-whole-archive) endif() +set(webnn_srcs "") +if(HAVE_WEBNN) + list(APPEND include_dirs ${WEBNN_HEADER_DIRS}) + list(APPEND include_dirs ${WEBNN_INCLUDE_DIRS}) + list(APPEND libs -Wl,--whole-archive ${WEBNN_LIBRARIES} -Wl,--no-whole-archive) + list(APPEND webnn_srcs $ENV{WEBNN_NATIVE_DIR}/gen/src/webnn/webnn_cpp.cpp) +endif() + ocv_module_include_directories(${include_dirs}) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") ocv_append_source_files_cxx_compiler_options(fw_srcs "-Wno-suggest-override") # GCC @@ -162,7 +170,7 @@ if(HAVE_NGRAPH) list(APPEND dnn_runtime_libs ngraph::ngraph) endif() -ocv_glob_module_sources(${sources_options} SOURCES ${fw_srcs}) +ocv_glob_module_sources(${sources_options} SOURCES ${fw_srcs} ${webnn_srcs}) ocv_create_module(${libs} ${dnn_runtime_libs}) ocv_add_samples() ocv_add_accuracy_tests(${dnn_runtime_libs}) diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 4146a0c1e8ce..31c2f48611c1 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -95,8 +95,7 @@ CV__DNN_INLINE_NS_BEGIN DNN_TARGET_FPGA, //!< FPGA device with CPU fallbacks using Inference Engine's Heterogeneous plugin. DNN_TARGET_CUDA, DNN_TARGET_CUDA_FP16, - DNN_TARGET_HDDL, - DNN_TARGET_GPU + DNN_TARGET_HDDL }; CV_EXPORTS std::vector< std::pair > getAvailableBackends(); @@ -300,6 +299,8 @@ CV__DNN_INLINE_NS_BEGIN virtual Ptr initVkCom(const std::vector > &inputs); + virtual Ptr initWebnn(const std::vector > &inputs, const std::vector >& nodes); + /** * @brief Returns a CUDA backend node * diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index bf2be2301842..9f49596df089 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -233,8 +233,11 @@ class BackendRegistry #endif // HAVE_INF_ENGINE #ifdef HAVE_WEBNN - if (haveWebNN()) + if (haveWebnn()) + { backends.push_back(std::make_pair(DNN_BACKEND_WEBNN, DNN_TARGET_CPU)); + backends.push_back(std::make_pair(DNN_BACKEND_WEBNN, DNN_TARGET_OPENCL)); + } #endif // HAVE_WEBNN #ifdef HAVE_OPENCL @@ -1131,8 +1134,7 @@ static Ptr wrapMat(int backendId, int targetId, cv::Mat& m) else if (backendId == DNN_BACKEND_WEBNN) { #ifdef HAVE_WEBNN - CV_Assert(haveWebNN()); - return Ptr(new WebNNBackendWrapper(targetId, m)); + return Ptr(new WebnnBackendWrapper(targetId, m)); #else CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of WebNN"); #endif @@ -1429,7 +1431,7 @@ struct Net::Impl : public detail::NetImplBase if (preferableBackend == DNN_BACKEND_WEBNN) { CV_Assert(preferableTarget == DNN_TARGET_CPU || - preferableTarget == DNN_TARGET_GPU); + preferableTarget == DNN_TARGET_OPENCL); } #endif CV_Assert(preferableBackend != DNN_BACKEND_VKCOM || @@ -1659,7 +1661,7 @@ struct Net::Impl : public detail::NetImplBase else if (preferableBackend == DNN_BACKEND_WEBNN) { #ifdef HAVE_WEBNN - initWebNNBackend(blobsToKeep_); + initWebnnBackend(blobsToKeep_); #else CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of WebNN"); #endif @@ -2368,10 +2370,255 @@ struct Net::Impl : public detail::NetImplBase } #endif // HAVE_DNN_NGRAPH - void initWebNNBackend(const std::vector& blobsToKeep_) - { #ifdef HAVE_WEBNN - // to do + void addWebnnOutputs(LayerData &ld) + { + CV_TRACE_FUNCTION(); + + Ptr layerNet; + auto it = ld.backendNodes.find(preferableBackend); + if (it != ld.backendNodes.end()) + { + Ptr node = it->second; + if (!node.empty()) + { + Ptr webnnNode = node.dynamicCast(); + CV_Assert(!webnnNode.empty()); CV_Assert(!webnnNode->net.empty()); + layerNet = webnnNode->net; + } + } + + for (int i = 0; i < ld.inputBlobsId.size(); ++i) + { + LayerData &inpLd = layers[ld.inputBlobsId[i].lid]; + Ptr inpNode = inpLd.backendNodes[preferableBackend]; + if (!inpNode.empty()) + { + Ptr webnnInpNode = inpNode.dynamicCast(); + CV_Assert(!webnnInpNode.empty()); CV_Assert(!webnnInpNode->net.empty()); + if (layerNet != webnnInpNode->net) + { + webnnInpNode->net->addOutput(webnnInpNode->name); + webnnInpNode->net->setUnconnectedNodes(webnnInpNode); + } + } + } + } + + void initWebnnBackend(const std::vector& blobsToKeep_) + { + CV_TRACE_FUNCTION(); + CV_Assert_N(preferableBackend == DNN_BACKEND_WEBNN, haveWebnn()); + + MapIdToLayerData::iterator it; + Ptr net; + + for (it = layers.begin(); it != layers.end(); ++it) + { + LayerData &ld = it->second; + if (ld.id == 0) + { + CV_Assert((netInputLayer->outNames.empty() && ld.outputBlobsWrappers.size() == 1) || + (netInputLayer->outNames.size() == ld.outputBlobsWrappers.size())); + for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) + { + Ptr wrapper = ld.outputBlobsWrappers[i].dynamicCast(); + std::string outputName = netInputLayer->outNames.empty() ? ld.name : netInputLayer->outNames[i]; + outputName = ld.outputBlobsWrappers.size() > 1 ? (outputName + "." + std::to_string(i)) : outputName; + wrapper->name = outputName; + } + } + else + { + for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) + { + Ptr wrapper = ld.outputBlobsWrappers[i].dynamicCast(); + std::string outputName = ld.outputBlobsWrappers.size() > 1 ? (ld.name + "." + std::to_string(i)) : ld.name; + wrapper->name = outputName; + } + } + } + + // Build WebNN networks from sets of layers that support this + // backend. Split a whole model on several WebNN networks if + // some of layers are not implemented. + for (it = layers.begin(); it != layers.end(); ++it) + { + LayerData &ld = it->second; + + if (ld.id == 0 && ld.skip) + continue; + + bool fused = ld.skip; + Ptr layer = ld.layerInstance; + if (!fused && !layer->supportBackend(preferableBackend)) + { + addWebnnOutputs(ld); + net = Ptr(); + layer->preferableTarget = DNN_TARGET_CPU; + + for (int i = 0; i < ld.inputBlobsId.size(); ++i) + { + LayerData &inpLd = layers[ld.inputBlobsId[i].lid]; + Ptr inpNode = inpLd.backendNodes[preferableBackend]; + if (!inpNode.empty()) { + Ptr webnnNode = inpNode.dynamicCast(); + CV_Assert(!webnnNode.empty()); + webnnNode->net->setUnconnectedNodes(webnnNode); + } + } + continue; + } + ld.skip = true; // Initially skip all WebNN supported layers. + + // Create a new network if one of inputs from different WebNN graph. + std::vector> inputNodes; + for (int i = 0; i < ld.inputBlobsId.size(); ++i) + { + // Layer_Test_ROIPooling.Accuracy has 2 inputs inpLD = 0, 0 -> has 4 inputNodes (input, rois, input, rois) + if (inputNodes.size() == ld.inputBlobsId.size()) { + break; + } + LayerData &inpLd = layers[ld.inputBlobsId[i].lid]; + Ptr inpNode = inpLd.backendNodes[preferableBackend]; + if (!inpNode.empty()) + { + Ptr webnnInpNode = inpNode.dynamicCast(); + CV_Assert(!webnnInpNode.empty()); CV_Assert(!webnnInpNode->net.empty()); + if (webnnInpNode->net == net && !fused) { + inputNodes.push_back(inpNode); + continue; + } + } + + if (net.empty()) { + net = Ptr(new WebnnNet()); + } + + if (!fused) { + std::vector inputNames; + std::vector inputs; + + auto curr_pos = inpLd.consumers.begin(); + auto compare = [&ld] (const LayerPin& lp) { return lp.lid == ld.id; }; + auto cons = curr_pos; + while ((cons = std::find_if(curr_pos, inpLd.consumers.end(), compare)) != + inpLd.consumers.end()) { + int cons_inp = cons->oid; + Ptr inpWrapper = inpLd.outputBlobsWrappers[cons_inp]. + dynamicCast(); + CV_Assert(!inpWrapper.empty()); + auto iter = std::find(inputNames.begin(), inputNames.end(), + inpWrapper->name); + if (iter == inputNames.end()) { + inputNames.push_back(inpWrapper->name); + inputs.push_back(inpLd.outputBlobs[cons_inp]); + } + curr_pos = cons + 1; + } + + auto inps = net->setInputs(inputs, inputNames); + for (auto& inp : inps) { + inputNodes.emplace_back(Ptr(new WebnnBackendNode(inp))); + } + } + } + + Ptr node; + if (!net.empty()) + { + if (fused) + { + bool inPlace = ld.inputBlobsId.size() == 1 && ld.outputBlobs.size() == 1 && + ld.inputBlobs[0]->data == ld.outputBlobs[0].data; + CV_Assert(inPlace); + node = layers[ld.inputBlobsId[0].lid].backendNodes[preferableBackend]; + ld.inputBlobsWrappers = layers[ld.inputBlobsId[0].lid].inputBlobsWrappers; + } + } + else { + net = Ptr(new WebnnNet()); + } + + if (!fused) + { + CV_Assert(ld.inputBlobsId.size() == inputNodes.size()); + for (int i = 0; i < ld.inputBlobsId.size(); ++i) + { + int lid = ld.inputBlobsId[i].lid; + int oid = ld.inputBlobsId[i].oid; + if (oid == 0 || lid == 0) + continue; + + auto webnnInpNode = inputNodes[i].dynamicCast(); + inputNodes[i] = Ptr(new WebnnBackendNode(webnnInpNode->operand)); + } + + if (layer->supportBackend(preferableBackend)) + { + node = layer->initWebnn(ld.inputBlobsWrappers, inputNodes); + for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) + { + Ptr wrapper = ld.outputBlobsWrappers[i].dynamicCast(); + node.dynamicCast()->name = wrapper->name; + } + } + else + { + continue; + } + } + else if (node.empty()) + continue; + + ld.backendNodes[preferableBackend] = node; + + Ptr webnnNode = node.dynamicCast(); + CV_Assert(!webnnNode.empty()); + webnnNode->net = net; + + if (ld.consumers.empty()) { + // TF EAST_text_detection + webnnNode->net->setUnconnectedNodes(webnnNode); + } + for (const auto& pin : blobsToKeep_) + { + if (pin.lid == ld.id) + { + webnnNode->net->addOutput(webnnNode->name); + break; + } + } + net->addBlobs(ld.inputBlobsWrappers); + net->addBlobs(ld.outputBlobsWrappers); + addWebnnOutputs(ld); + } + + // Initialize all networks. + for (MapIdToLayerData::reverse_iterator it = layers.rbegin(); it != layers.rend(); ++it) + { + LayerData &ld = it->second; + auto iter = ld.backendNodes.find(preferableBackend); + if (iter == ld.backendNodes.end()) + continue; + + Ptr& node = iter->second; + if (node.empty()) + continue; + + Ptr webnnNode = node.dynamicCast(); + if (webnnNode.empty()) + continue; + + CV_Assert(!webnnNode->net.empty()); + + if (!webnnNode->net->isInitialized()) + { + webnnNode->net->setUnconnectedNodes(webnnNode); + webnnNode->net->createNet((Target)preferableTarget); + ld.skip = false; + } + } #endif } @@ -3416,7 +3663,7 @@ struct Net::Impl : public detail::NetImplBase } else if (preferableBackend == DNN_BACKEND_WEBNN) { - forwardWebNN(ld.outputBlobsWrappers, node, isAsync); + forwardWebnn(ld.outputBlobsWrappers, node, isAsync); } else if (preferableBackend == DNN_BACKEND_VKCOM) { @@ -4534,6 +4781,7 @@ string Net::Impl::dump() case DNN_BACKEND_OPENCV: backend = "OCV/"; break; case DNN_BACKEND_VKCOM: backend = "VULKAN/"; break; case DNN_BACKEND_CUDA: backend = "CUDA/"; break; + case DNN_BACKEND_WEBNN: backend = "WEBNN/"; break; // don't use default: } out << "digraph G {\n"; @@ -5125,7 +5373,7 @@ Ptr Layer::initNgraph(const std::vector > & inp return Ptr(); } -Ptr Layer::initWebNN(const std::vector > & inputs, const std::vector >& nodes) +Ptr Layer::initWebnn(const std::vector > & inputs, const std::vector >& nodes) { CV_Error(Error::StsNotImplemented, "WebNN pipeline of " + type + " layers is not defined."); diff --git a/modules/dnn/src/op_webnn.cpp b/modules/dnn/src/op_webnn.cpp index 937158a327de..640c1f3fdda1 100644 --- a/modules/dnn/src/op_webnn.cpp +++ b/modules/dnn/src/op_webnn.cpp @@ -20,6 +20,8 @@ namespace cv { namespace dnn { #ifdef HAVE_WEBNN +static std::string kDefaultInpLayerName = "opencv_webnn_empty_inp_layer_name"; + template static inline std::vector getShape(const Mat& mat) { @@ -29,110 +31,150 @@ static inline std::vector getShape(const Mat& mat) return result; } -// WebnnGraph - -WebnnGraph::WebnnGraph() +static std::vector > +webnnWrappers(const std::vector >& ptrs) { - + std::vector > wrappers(ptrs.size()); + for (int i = 0; i < ptrs.size(); ++i) + { + CV_Assert(!ptrs[i].empty()); + wrappers[i] = ptrs[i].dynamicCast(); + CV_Assert(!wrappers[i].empty()); + } + return wrappers; } -bool WebnnGraph::isInitialized() +// WebnnNet +WebnnNet::WebnnNet() { - + hasNetOwner = false; + device_name = "CPU"; } -void WebnnGraph::init(Target targetId) +void WebnnNet::addOutput(const std::string& name) { + requestedOutputs.push_back(name); +} +void WebnnNet::createNet(Target targetId) { + init(targetId); } -void WebnnGraph::forward(const std::vector >& outBlobsWrappers) +void WebnnNet::init(Target targetId) { + switch (targetId) + { + case DNN_TARGET_CPU: + device_name = "CPU"; + break; + case DNN_TARGET_OPENCL: + device_name = "GPU"; + break; + default: + CV_Error(Error::StsNotImplemented, "Unknown target"); + }; + + CV_Error(Error::StsNotImplemented, "Create ml::Graph"); +} + +std::vector WebnnNet::setInputs(const std::vector& inputs, + const std::vector& names) { + CV_Assert_N(inputs.size() == names.size()); + std::vector current_inp; + for (size_t i = 0; i < inputs.size(); i++) + { + CV_Error(Error::StsNotImplemented, "Create ml::Operand"); + } + return current_inp; +} +void WebnnNet::setUnconnectedNodes(Ptr& node) { + unconnectedNodes.push_back(node); } -void WebnnGraph::createGraph(Target targetId) +bool WebnnNet::isInitialized() { + return isInit; +} +void WebnnNet::reset() +{ + allBlobs.clear(); + isInit = false; } -// WebnnBackendNode +void WebnnNet::addBlobs(const std::vector >& ptrs) +{ + auto wrappers = webnnWrappers(ptrs); + for (const auto& wrapper : wrappers) + { + std::string name = wrapper->name; + name = name.empty() ? kDefaultInpLayerName : name; + allBlobs.insert({name, wrapper}); + } +} -WebnnBackendNode::WebnnBackendNode(const std::vector >& nodes, - Ptr& cvLayer_, std::vector& inputs, - std::vector& outputs, std::vector& internals) - : BackendNode(DNN_BACKEND_WEBNN)) +void WebnnNet::forward(const std::vector >& outBlobsWrappers, bool isAsync) { - // to do. Not necessary now. + CV_LOG_DEBUG(NULL, "WebnnNet::forward(" << (isAsync ? "async" : "sync") << ")"); + CV_Error(Error::StsNotImplemented, "Implement ml::Graph.compute"); } -WebnnBackendNode::WebnnBackendNode(std::shared_ptr&& _operand) +// WebnnBackendNode +WebnnBackendNode::WebnnBackendNode(ml::Operand&& _operand) : BackendNode(DNN_BACKEND_WEBNN), operand(std::move(_operand)) {} -WebnnBackendNode::WebnnBackendNode(std::shared_ptr&& _operand) +WebnnBackendNode::WebnnBackendNode(ml::Operand& _operand) : BackendNode(DNN_BACKEND_WEBNN), operand(_operand) {} // WebnnBackendWrapper - -WebnnBackendWrapper::WebnnBackendWrapper(const Mat& m) +WebnnBackendWrapper::WebnnBackendWrapper(int targetId, const cv::Mat& m) + : BackendWrapper(DNN_BACKEND_WEBNN, targetId) { - dimensions = getShape(m); - if (m.type() == CV_16F) - { - dataSize = m.total() * m.elemSize(); - buffer.reset(new char[dataSize]); - std::memcpy(buffer.get(), (float16_t*)m.data, dataSize); - descriptor = {ml::Operand::Float16, dimensions.data(), dimensions.size()}; - } - else if (m.type() == CV_32F) + size_t dataSize = m.total() * m.elemSize(); + buffer.reset(new char[dataSize]); + std::memcpy(buffer.get(), m.data, dataSize); + dimensions = getShape(m); + descriptor.dimensions = dimensions.data(); + descriptor.dimensionsCount = dimensions.size(); + if (m.type() == CV_32F) { - dataSize = m.total() * m.elemSize(); - buffer.reset(new char[dataSize]); - std::memcpy(buffer.get(), (float32_t*)m.data, dataSize); - descriptor = {ml::Operand::Float32, dimensions.data(), dimensions.size()}; - } - else if (m.type() == CV_8U) - { - dataSize = m.total() * m.elemSize(); - buffer.reset(new char[dataSize]); - std::memcpy(buffer.get(), (uint8_t*)m.data, dataSize); - descriptor = {ml::Operand::Uint8, dimensions.data(), dimensions.size()}; + descriptor.type = ml::OperandType::Float32; } else CV_Error(Error::StsNotImplemented, format("Unsupported data type %s", typeToString(m.type()).c_str())); } -WebnnBackendWrapper::WebnnBackendWrapper(Ptr wrapper) +WebnnBackendWrapper::~WebnnBackendWrapper() { - Ptr webnnWrapper = wrapper.dynamicCast(); - CV_Assert(!webnnWrapper.empty()); - buffer = webnnWrapper->buffer; - descriptor = webnnWrapper->descriptor; -} - -static Ptr WebnnBackendWrapper::create(Ptr wrapper) -{ - return Ptr(new WebnnBackendWrapper(wrapper)); + // nothing } void WebnnBackendWrapper::copyToHost() { CV_LOG_DEBUG(NULL, "WebnnBackendWrapper::copyToHost()"); + //CV_Error(Error::StsNotImplemented, ""); } void WebnnBackendWrapper::setHostDirty() { CV_LOG_DEBUG(NULL, "WebnnBackendWrapper::setHostDirty()"); + //CV_Error(Error::StsNotImplemented, ""); } -void * WebnnBackendWrapper::getBuffer() +void forwardWebnn(const std::vector >& outBlobsWrappers, + Ptr& node, bool isAsync) { - return buffer; + CV_Assert(!node.empty()); + Ptr webnnNode = node.dynamicCast(); + CV_Assert(!webnnNode.empty()); + webnnNode->net->forward(outBlobsWrappers, isAsync); } + #else void forwardWebnn(const std::vector >& outBlobsWrappers, - Ptr& operand) + Ptr& operand, bool isAsync) { CV_Assert(false && "WebNN is not enabled in this OpenCV build"); } diff --git a/modules/dnn/src/op_webnn.hpp b/modules/dnn/src/op_webnn.hpp index 6f1c5cd91636..b2263ab26673 100644 --- a/modules/dnn/src/op_webnn.hpp +++ b/modules/dnn/src/op_webnn.hpp @@ -19,11 +19,13 @@ #include #include +#include + #endif // HAVE_WEBNN namespace cv { namespace dnn { -constexpr bool haveWebNN() { +constexpr bool haveWebnn() { #ifdef HAVE_WEBNN return true; #else @@ -34,63 +36,76 @@ constexpr bool haveWebNN() { #ifdef HAVE_WEBNN class WebnnBackendNode; +class WebnnBackendWrapper; - -class WebnnGraph +class WebnnNet { public: - WebnnGraph(); + WebnnNet(); - bool isInitialized(); + void addOutput(const std::string& name); + bool isInitialized(); void init(Target targetId); - void forward(const std::vector >& outBlobsWrappers); + void forward(const std::vector >& outBlobsWrappers, bool isAsync); + + std::vector setInputs(const std::vector& inputs, const std::vector& names); + + void setUnconnectedNodes(Ptr& node); + void addBlobs(const std::vector >& ptrs); - void createGraph(Target targetId); + void createNet(Target targetId); + // void setNodePtr(std::shared_ptr* ptr); + + void reset(); private: ml::GraphBuilder builder; - ml::Context mContext; - ml::Graph mGraph; - ml::NamedOperands mResults; + ml::Context context; + ml::Graph graph; + + std::unordered_map> allBlobs; + + bool hasNetOwner; + std::string device_name; bool isInit = false; + + std::vector requestedOutputs; + std::vector> unconnectedNodes; }; class WebnnBackendNode : public BackendNode { public: - WebnnBackendNode(const std::vector >& nodes, Ptr& layer, - std::vector& inputs, std::vector& outputs, - std::vector& internals); + WebnnBackendNode(ml::Operand&& operand); + WebnnBackendNode(ml::Operand& operand); + std::string name; ml::Operand operand; - WebnnGraph graph; + Ptr net; + Ptr cvLayer; }; class WebnnBackendWrapper : public BackendWrapper { public: - WebnnBackendWrapper(const Mat& m); - WebnnBackendWrapper(Ptr wrapper); - // ~WebnnBackendWrapper(); - - static Ptr create(Ptr wrapper); + WebnnBackendWrapper(int targetId, const Mat& m); + ~WebnnBackendWrapper(); virtual void copyToHost() CV_OVERRIDE; virtual void setHostDirty() CV_OVERRIDE; - virtual void * getBuffer(); -private: + std::string name; std::unique_ptr buffer; + std::vector dimensions; ml::OperandDescriptor descriptor; - std::vector dimensions; }; #endif // HAVE_WebNN void forwardWebnn(const std::vector >& outBlobsWrappers, - Ptr& operand); + Ptr& node, bool isAsync); }} // namespace cv::dnn diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp index 139f3d1671b1..f20aa507c100 100644 --- a/modules/dnn/test/test_common.hpp +++ b/modules/dnn/test/test_common.hpp @@ -135,7 +135,8 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget bool withCpuOCV = true, bool withVkCom = true, bool withCUDA = true, - bool withNgraph = true + bool withNgraph = true, + bool withWebnn = true ); testing::internal::ParamGenerator< tuple > dnnBackendsAndTargetsIE(); diff --git a/modules/dnn/test/test_common.impl.hpp b/modules/dnn/test/test_common.impl.hpp index 3d56e6f30875..c312474256f2 100644 --- a/modules/dnn/test/test_common.impl.hpp +++ b/modules/dnn/test/test_common.impl.hpp @@ -29,6 +29,7 @@ void PrintTo(const cv::dnn::Backend& v, std::ostream* os) case DNN_BACKEND_CUDA: *os << "CUDA"; return; case DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019: *os << "DLIE"; return; case DNN_BACKEND_INFERENCE_ENGINE_NGRAPH: *os << "NGRAPH"; return; + case DNN_BACKEND_WEBNN: *os << "WEBNN"; return; } // don't use "default:" to emit compiler warnings *os << "DNN_BACKEND_UNKNOWN(" << (int)v << ")"; } @@ -247,7 +248,8 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget bool withCpuOCV /*= true*/, bool withVkCom /*= true*/, bool withCUDA /*= true*/, - bool withNgraph /*= true*/ + bool withNgraph /*= true*/, + bool withWebnn /*= false*/ ) { #ifdef HAVE_INF_ENGINE @@ -302,6 +304,17 @@ testing::internal::ParamGenerator< tuple > dnnBackendsAndTarget } #endif +#ifdef HAVE_WEBNN + if (withWebnn) + { + for (auto target : getAvailableTargets(DNN_BACKEND_WEBNN)) { + targets.push_back(make_tuple(DNN_BACKEND_WEBNN, target)); + } + } +#else + CV_UNUSED(withWebnn); +#endif + { available = getAvailableTargets(DNN_BACKEND_OPENCV); for (std::vector< Target >::const_iterator i = available.begin(); i != available.end(); ++i)