Path: blob/master/modules/gapi/src/compiler/gcompiler.cpp
16337 views
// This file is part of OpenCV project.1// It is subject to the license terms in the LICENSE file found in the top-level directory2// of this distribution and at http://opencv.org/license.html.3//4// Copyright (C) 2018 Intel Corporation567#include "precomp.hpp"89#include <vector>10#include <stack>11#include <unordered_map>1213#include <ade/util/algorithm.hpp> // any_of14#include <ade/util/zip_range.hpp> // zip_range, indexed1516#include <ade/graph.hpp>17#include <ade/passes/check_cycles.hpp>1819#include "api/gcomputation_priv.hpp"20#include "api/gnode_priv.hpp" // FIXME: why it is here?21#include "api/gproto_priv.hpp" // FIXME: why it is here?22#include "api/gcall_priv.hpp" // FIXME: why it is here?23#include "api/gapi_priv.hpp" // FIXME: why it is here?24#include "api/gbackend_priv.hpp" // Backend basic API (newInstance, etc)2526#include "compiler/gmodel.hpp"27#include "compiler/gmodelbuilder.hpp"28#include "compiler/gcompiler.hpp"29#include "compiler/gcompiled_priv.hpp"30#include "compiler/passes/passes.hpp"3132#include "executor/gexecutor.hpp"33#include "backends/common/gbackend.hpp"3435// <FIXME:>36#if !defined(GAPI_STANDALONE)37#include "opencv2/gapi/cpu/core.hpp" // Also directly refer to Core38#include "opencv2/gapi/cpu/imgproc.hpp" // ...and Imgproc kernel implementations39#endif // !defined(GAPI_STANDALONE)40// </FIXME:>4142#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend()4344#include "logger.hpp"4546namespace47{48cv::gapi::GKernelPackage getKernelPackage(cv::GCompileArgs &args)49{50static auto ocv_pkg =51#if !defined(GAPI_STANDALONE)52combine(cv::gapi::core::cpu::kernels(),53cv::gapi::imgproc::cpu::kernels(),54cv::unite_policy::KEEP);55#else56cv::gapi::GKernelPackage();57#endif // !defined(GAPI_STANDALONE)58auto user_pkg = cv::gimpl::getCompileArg<cv::gapi::GKernelPackage>(args);59return combine(ocv_pkg, user_pkg.value_or(cv::gapi::GKernelPackage{}), cv::unite_policy::REPLACE);60}6162cv::util::optional<std::string> getGraphDumpDirectory(cv::GCompileArgs& args)63{64auto dump_info = cv::gimpl::getCompileArg<cv::graph_dump_path>(args);65if (!dump_info.has_value())66{67const char* path = std::getenv("GRAPH_DUMP_PATH");68return path69? cv::util::make_optional(std::string(path))70: cv::util::optional<std::string>();71}72else73{74return cv::util::make_optional(dump_info.value().m_dump_path);75}76}77} // anonymous namespace787980// GCompiler implementation ////////////////////////////////////////////////////8182cv::gimpl::GCompiler::GCompiler(const cv::GComputation &c,83GMetaArgs &&metas,84GCompileArgs &&args)85: m_c(c), m_metas(std::move(metas)), m_args(std::move(args))86{87using namespace std::placeholders;88m_all_kernels = getKernelPackage(m_args);89auto lookup_order = getCompileArg<gapi::GLookupOrder>(m_args).value_or(gapi::GLookupOrder());90auto dump_path = getGraphDumpDirectory(m_args);9192m_e.addPassStage("init");93m_e.addPass("init", "check_cycles", ade::passes::CheckCycles());94m_e.addPass("init", "expand_kernels", std::bind(passes::expandKernels, _1,95m_all_kernels)); // NB: package is copied96m_e.addPass("init", "topo_sort", ade::passes::TopologicalSort());97m_e.addPass("init", "init_islands", passes::initIslands);98m_e.addPass("init", "check_islands", passes::checkIslands);99// TODO:100// - Check basic graph validity (i.e., all inputs are connected)101// - Complex dependencies (i.e. parent-child) unrolling102// - etc, etc, etc103104// Remove GCompoundBackend to avoid calling setupBackend() with it in the list105m_all_kernels.remove(cv::gapi::compound::backend());106m_e.addPass("init", "resolve_kernels", std::bind(passes::resolveKernels, _1,107std::ref(m_all_kernels), // NB: and not copied here108lookup_order));109110m_e.addPass("init", "check_islands_content", passes::checkIslandsContent);111m_e.addPassStage("meta");112m_e.addPass("meta", "initialize", std::bind(passes::initMeta, _1, std::ref(m_metas)));113m_e.addPass("meta", "propagate", passes::inferMeta);114m_e.addPass("meta", "finalize", passes::storeResultingMeta);115// moved to another stage, FIXME: two dumps?116// m_e.addPass("meta", "dump_dot", passes::dumpDotStdout);117118// Special stage for backend-specific transformations119// FIXME: document passes hierarchy and order for backend developers120m_e.addPassStage("transform");121122m_e.addPassStage("exec");123m_e.addPass("exec", "fuse_islands", passes::fuseIslands);124m_e.addPass("exec", "sync_islands", passes::syncIslandTags);125126if (dump_path.has_value())127{128m_e.addPass("exec", "dump_dot", std::bind(passes::dumpGraph, _1,129dump_path.value()));130}131132// Process backends at the last moment (after all G-API passes are added).133ade::ExecutionEngineSetupContext ectx(m_e);134auto backends = m_all_kernels.backends();135for (auto &b : backends)136{137b.priv().addBackendPasses(ectx);138}139}140141void cv::gimpl::GCompiler::validateInputMeta()142{143if (m_metas.size() != m_c.priv().m_ins.size())144{145util::throw_error(std::logic_error146("COMPILE: GComputation interface / metadata mismatch! "147"(expected " + std::to_string(m_c.priv().m_ins.size()) + ", "148"got " + std::to_string(m_metas.size()) + " meta arguments)"));149}150151const auto meta_matches = [](const GMetaArg &meta, const GProtoArg &proto) {152switch (proto.index())153{154// FIXME: Auto-generate methods like this from traits:155case GProtoArg::index_of<cv::GMat>():156return util::holds_alternative<cv::GMatDesc>(meta);157158case GProtoArg::index_of<cv::GScalar>():159return util::holds_alternative<cv::GScalarDesc>(meta);160161case GProtoArg::index_of<cv::detail::GArrayU>():162return util::holds_alternative<cv::GArrayDesc>(meta);163164default:165GAPI_Assert(false);166}167return false; // should never happen168};169170for (const auto &meta_arg_idx : ade::util::indexed(ade::util::zip(m_metas, m_c.priv().m_ins)))171{172const auto &meta = std::get<0>(ade::util::value(meta_arg_idx));173const auto &proto = std::get<1>(ade::util::value(meta_arg_idx));174175if (!meta_matches(meta, proto))176{177const auto index = ade::util::index(meta_arg_idx);178util::throw_error(std::logic_error179("GComputation object type / metadata descriptor mismatch "180"(argument " + std::to_string(index) + ")"));181// FIXME: report what we've got and what we've expected182}183}184// All checks are ok185}186187void cv::gimpl::GCompiler::validateOutProtoArgs()188{189for (const auto &out_pos : ade::util::indexed(m_c.priv().m_outs))190{191const auto &node = proto::origin_of(ade::util::value(out_pos)).node;192if (node.shape() != cv::GNode::NodeShape::CALL)193{194auto pos = ade::util::index(out_pos);195util::throw_error(std::logic_error196("Computation output " + std::to_string(pos) +197" is not a result of any operation"));198}199}200}201202cv::gimpl::GCompiler::GPtr cv::gimpl::GCompiler::generateGraph()203{204validateInputMeta();205validateOutProtoArgs();206207// Generate ADE graph from expression-based computation208std::unique_ptr<ade::Graph> pG(new ade::Graph);209ade::Graph& g = *pG;210211GModel::Graph gm(g);212cv::gimpl::GModel::init(gm);213cv::gimpl::GModelBuilder builder(g);214auto proto_slots = builder.put(m_c.priv().m_ins, m_c.priv().m_outs);215GAPI_LOG_INFO(NULL, "Generated graph: " << g.nodes().size() << " nodes" << std::endl);216217// Store Computation's protocol in metadata218Protocol p;219std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;220gm.metadata().set(p);221222return pG;223}224225void cv::gimpl::GCompiler::runPasses(ade::Graph &g)226{227m_e.runPasses(g);228GAPI_LOG_INFO(NULL, "All compiler passes are successful");229}230231void cv::gimpl::GCompiler::compileIslands(ade::Graph &g)232{233GModel::Graph gm(g);234std::shared_ptr<ade::Graph> gptr(gm.metadata().get<IslandModel>().model);235GIslandModel::Graph gim(*gptr);236237// Run topological sort on GIslandModel first238auto pass_ctx = ade::passes::PassContext{*gptr};239ade::passes::TopologicalSort{}(pass_ctx);240241// Now compile islands242GIslandModel::compileIslands(gim, g, m_args);243}244245cv::GCompiled cv::gimpl::GCompiler::produceCompiled(GPtr &&pg)246{247// This is the final compilation step. Here:248// - An instance of GExecutor is created. Depening on the platform,249// build configuration, etc, a GExecutor may be:250// - a naive single-thread graph interpreter;251// - a std::thread-based thing252// - a TBB-based thing, etc.253// - All this stuff is wrapped into a GCompiled object and returned254// to user.255256// Note: this happens in the last pass ("compile_islands"):257// - Each GIsland of GIslandModel instantiates its own,258// backend-specific executable object259// - Every backend gets a subgraph to execute, and builds260// an execution plan for it (backend-specific execution)261// ...before call to produceCompiled();262263const auto &outMetas = GModel::ConstGraph(*pg).metadata()264.get<OutputMeta>().outMeta;265std::unique_ptr<GExecutor> pE(new GExecutor(std::move(pg)));266// FIXME: select which executor will be actually used,267// make GExecutor abstract.268269GCompiled compiled;270compiled.priv().setup(m_metas, outMetas, std::move(pE));271return compiled;272}273274cv::GCompiled cv::gimpl::GCompiler::compile()275{276std::unique_ptr<ade::Graph> pG = generateGraph();277runPasses(*pG);278compileIslands(*pG);279return produceCompiled(std::move(pG));280}281282283