diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.hpp b/modules/gapi/src/backends/fluid/gfluidbackend.hpp index 74336f20a3..f7821823bd 100644 --- a/modules/gapi/src/backends/fluid/gfluidbackend.hpp +++ b/modules/gapi/src/backends/fluid/gfluidbackend.hpp @@ -153,6 +153,8 @@ public: virtual void run(std::vector &&input_objs, std::vector &&output_objs) override; + using GIslandExecutable::run; // (IInput&, IOutput&) version + void run(std::vector &input_objs, std::vector &output_objs); diff --git a/modules/gapi/src/compiler/gislandmodel.cpp b/modules/gapi/src/compiler/gislandmodel.cpp index 75644fa25b..7262922191 100644 --- a/modules/gapi/src/compiler/gislandmodel.cpp +++ b/modules/gapi/src/compiler/gislandmodel.cpp @@ -12,6 +12,7 @@ #include #include +#include // zip_range, indexed #include "api/gbackend_priv.hpp" // GBackend::Priv().compile() #include "compiler/gmodel.hpp" @@ -319,5 +320,33 @@ ade::NodeHandle GIslandModel::producerOf(const ConstGraph &g, ade::NodeHandle &d return ade::NodeHandle(); } +void GIslandExecutable::run(GIslandExecutable::IInput &in, GIslandExecutable::IOutput &out) +{ + // Default implementation: just reuse the existing old-fashioned run + // Build a single synchronous execution frame for it. + std::vector in_objs; + std::vector out_objs; + const auto &in_desc = in.desc(); + const auto &out_desc = out.desc(); + const auto in_vector = in.get(); // FIXME: passing temporary objects to toRange() leads to issues + in_objs.reserve(in_desc.size()); + out_objs.reserve(out_desc.size()); + for (auto &&it: ade::util::zip(ade::util::toRange(in_desc), + ade::util::toRange(in_vector))) + { + in_objs.emplace_back(std::get<0>(it), std::get<1>(it)); + } + for (auto &&it: ade::util::indexed(ade::util::toRange(out_desc))) + { + out_objs.emplace_back(ade::util::value(it), + out.get(ade::util::checked_cast(ade::util::index(it)))); + } + run(std::move(in_objs), std::move(out_objs)); + for (auto &&it: out_objs) + { + out.post(std::move(it.second)); // report output objects as "ready" to the executor + } +} + } // namespace cv } // namespace gimpl diff --git a/modules/gapi/src/compiler/gislandmodel.hpp b/modules/gapi/src/compiler/gislandmodel.hpp index 5998115a74..975f35abf8 100644 --- a/modules/gapi/src/compiler/gislandmodel.hpp +++ b/modules/gapi/src/compiler/gislandmodel.hpp @@ -89,33 +89,58 @@ protected: util::optional m_user_tag; }; - - // GIslandExecutable - a backend-specific thing which executes // contents of an Island // * Is instantiated by the last step of the Islands fusion procedure; // * Is orchestrated by a GExecutor instance. // - class GIslandExecutable { public: using InObj = std::pair; using OutObj = std::pair; + class IODesc; + struct IInput; + struct IOutput; + // FIXME: now run() requires full input vector to be available. // actually, parts of subgraph may execute even if there's no all data // slots in place. // TODO: Add partial execution capabilities + // TODO: This method is now obsolette and is here for backwards + // compatibility only. Use (implement) the new run instead. virtual void run(std::vector &&input_objs, std::vector &&output_objs) = 0; + // Let the island execute. I/O data is obtained from/submitted to + // in/out objects. + virtual void run(IInput &in, IOutput &out); + virtual bool canReshape() const = 0; virtual void reshape(ade::Graph& g, const GCompileArgs& args) = 0; virtual ~GIslandExecutable() = default; }; +class GIslandExecutable::IODesc { + std::vector d; +public: + void set(std::vector &&newd) { d = std::move(newd); } + void set(const std::vector &newd) { d = newd; } + const std::vector &desc() const { return d; } +}; +struct GIslandExecutable::IInput: public GIslandExecutable::IODesc { + virtual ~IInput() = default; + virtual cv::GRunArgs get() = 0; // Get a new input vector (blocking) + virtual cv::GRunArgs try_get() = 0; // Get a new input vector (non-blocking) +}; +struct GIslandExecutable::IOutput: public GIslandExecutable::IODesc { + virtual ~IOutput() = default; + virtual GRunArgP get(int idx) = 0; // Allocate (wrap) a new data object for output idx + virtual void post(GRunArgP&&) = 0; // Release the object back to the framework (mark available) +}; + // GIslandEmitter - a backend-specific thing which feeds data into // the pipeline. This one is just an interface, implementations are executor-defined. class GIslandEmitter diff --git a/modules/gapi/src/executor/gexecutor.cpp b/modules/gapi/src/executor/gexecutor.cpp index d316f5bd41..74d107d908 100644 --- a/modules/gapi/src/executor/gexecutor.cpp +++ b/modules/gapi/src/executor/gexecutor.cpp @@ -122,6 +122,29 @@ void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh) } } +class cv::gimpl::GExecutor::Input final: public cv::gimpl::GIslandExecutable::IInput +{ + cv::gimpl::Mag &mag; + virtual cv::GRunArgs get() override + { + cv::GRunArgs res; + for (const auto &rc : desc()) { res.emplace_back(magazine::getArg(mag, rc)); } + return res; + } + virtual cv::GRunArgs try_get() override { return get(); } +public: + Input(cv::gimpl::Mag &m, const std::vector &rcs) : mag(m) { set(rcs); } +}; + +class cv::gimpl::GExecutor::Output final: public cv::gimpl::GIslandExecutable::IOutput +{ + cv::gimpl::Mag &mag; + virtual GRunArgP get(int idx) override { return magazine::getObjPtr(mag, desc()[idx]); } + virtual void post(GRunArgP&&) override { } // Do nothing here +public: + Output(cv::gimpl::Mag &m, const std::vector &rcs) : mag(m) { set(rcs); } +}; + void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) { // (2) @@ -144,8 +167,9 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) namespace util = ade::util; - //ensure that output Mat parameters are correctly allocated - for (auto index : util::iota(proto.out_nhs.size()) ) //FIXME: avoid copy of NodeHandle and GRunRsltComp ? + // ensure that output Mat parameters are correctly allocated + // FIXME: avoid copy of NodeHandle and GRunRsltComp ? + for (auto index : util::iota(proto.out_nhs.size())) { auto& nh = proto.out_nhs.at(index); const Data &d = m_gm.metadata(nh).get(); @@ -162,8 +186,9 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) }; #if !defined(GAPI_STANDALONE) - // Building as part of OpenCV - follow OpenCV behavior - // In the case of cv::Mat if output buffer is not enough to hold the result, reallocate it + // Building as part of OpenCV - follow OpenCV behavior In + // the case of cv::Mat if output buffer is not enough to + // hold the result, reallocate it if (cv::util::holds_alternative(args.outObjs.at(index))) { auto& out_mat = *get(args.outObjs.at(index)); @@ -204,24 +229,9 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) for (auto &op : m_ops) { // (5) - using InObj = GIslandExecutable::InObj; - using OutObj = GIslandExecutable::OutObj; - std::vector in_objs; - std::vector out_objs; - in_objs.reserve (op.in_objects.size()); - out_objs.reserve(op.out_objects.size()); - - for (const auto &rc : op.in_objects) - { - in_objs.emplace_back(InObj{rc, magazine::getArg(m_res, rc)}); - } - for (const auto &rc : op.out_objects) - { - out_objs.emplace_back(OutObj{rc, magazine::getObjPtr(m_res, rc)}); - } - - // (6) - op.isl_exec->run(std::move(in_objs), std::move(out_objs)); + Input i{m_res, op.in_objects}; + Output o{m_res, op.out_objects}; + op.isl_exec->run(i, o); } // (7) diff --git a/modules/gapi/src/executor/gexecutor.hpp b/modules/gapi/src/executor/gexecutor.hpp index e4128ba77f..a462e557a7 100644 --- a/modules/gapi/src/executor/gexecutor.hpp +++ b/modules/gapi/src/executor/gexecutor.hpp @@ -77,6 +77,9 @@ protected: }; std::vector m_slots; + class Input; + class Output; + Mag m_res; void initResource(const ade::NodeHandle &orig_nh); // FIXME: shouldn't it be RcDesc?