Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / thirdparty / fluid / modules / gapi / src / backends / fluid / gfluidbackend.cpp
1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 //
5 // Copyright (C) 2018-2019 Intel Corporation
6
7
8 #include "precomp.hpp"
9
10 #include <functional>
11 #include <iostream>
12 #include <iomanip> // std::fixed, std::setprecision
13 #include <unordered_set>
14 #include <stack>
15
16 #include <ade/util/algorithm.hpp>
17 #include <ade/util/chain_range.hpp>
18 #include <ade/util/range.hpp>
19 #include <ade/util/zip_range.hpp>
20
21 #include <ade/typed_graph.hpp>
22 #include <ade/execution_engine/execution_engine.hpp>
23
24 #include "opencv2/gapi/gcommon.hpp"
25 #include "logger.hpp"
26
27 #include "opencv2/gapi/own/convert.hpp"
28 #include "opencv2/gapi/gmat.hpp"    //for version of descr_of
29 // PRIVATE STUFF!
30 #include "compiler/gobjref.hpp"
31 #include "compiler/gmodel.hpp"
32
33 #include "backends/fluid/gfluidbuffer_priv.hpp"
34 #include "backends/fluid/gfluidbackend.hpp"
35
36 #include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
37
38 // FIXME: Is there a way to take a typed graph (our GModel),
39 // and create a new typed graph _ATOP_ of that (by extending with a couple of
40 // new types?).
41 // Alternatively, is there a way to compose types graphs?
42 //
43 // If not, we need to introduce that!
44 using GFluidModel = ade::TypedGraph
45     < cv::gimpl::FluidUnit
46     , cv::gimpl::FluidData
47     , cv::gimpl::Protocol
48     , cv::gimpl::FluidUseOwnBorderBuffer
49     >;
50
51 // FIXME: Same issue with Typed and ConstTyped
52 using GConstFluidModel = ade::ConstTypedGraph
53     < cv::gimpl::FluidUnit
54     , cv::gimpl::FluidData
55     , cv::gimpl::Protocol
56     , cv::gimpl::FluidUseOwnBorderBuffer
57     >;
58
59 // FluidBackend middle-layer implementation ////////////////////////////////////
60 namespace
61 {
62     class GFluidBackendImpl final: public cv::gapi::GBackend::Priv
63     {
64         virtual void unpackKernel(ade::Graph            &graph,
65                                   const ade::NodeHandle &op_node,
66                                   const cv::GKernelImpl &impl) override
67         {
68             GFluidModel fm(graph);
69             auto fluid_impl = cv::util::any_cast<cv::GFluidKernel>(impl.opaque);
70             fm.metadata(op_node).set(cv::gimpl::FluidUnit{fluid_impl, {}, 0, 0, 0.0});
71         }
72
73         virtual EPtr compile(const ade::Graph &graph,
74                              const cv::GCompileArgs &args,
75                              const std::vector<ade::NodeHandle> &nodes) const override
76         {
77             using namespace cv::gimpl;
78             GModel::ConstGraph g(graph);
79             auto isl_graph = g.metadata().get<IslandModel>().model;
80             GIslandModel::Graph gim(*isl_graph);
81
82             const auto num_islands = std::count_if
83                 (gim.nodes().begin(), gim.nodes().end(),
84                  [&](const ade::NodeHandle &nh) {
85                     return gim.metadata(nh).get<NodeKind>().k == NodeKind::ISLAND;
86                 });
87
88             const auto out_rois = cv::gimpl::getCompileArg<cv::GFluidOutputRois>(args);
89             if (num_islands > 1 && out_rois.has_value())
90                 cv::util::throw_error(std::logic_error("GFluidOutputRois feature supports only one-island graphs"));
91
92             auto rois = out_rois.value_or(cv::GFluidOutputRois());
93             return EPtr{new cv::gimpl::GFluidExecutable(graph, nodes, std::move(rois.rois))};
94         }
95
96         virtual void addBackendPasses(ade::ExecutionEngineSetupContext &ectx) override;
97
98     };
99 }
100
101 cv::gapi::GBackend cv::gapi::fluid::backend()
102 {
103     static cv::gapi::GBackend this_backend(std::make_shared<GFluidBackendImpl>());
104     return this_backend;
105 }
106
107 // FluidAgent implementation ///////////////////////////////////////////////////
108
109 namespace cv { namespace gimpl {
110 struct FluidMapper
111 {
112     FluidMapper(double ratio, int lpi) : m_ratio(ratio), m_lpi(lpi) {}
113     virtual ~FluidMapper() = default;
114     virtual int firstWindow(int outCoord, int lpi) const = 0;
115     virtual std::pair<int,int> linesReadAndNextWindow(int outCoord, int lpi) const = 0;
116
117 protected:
118     double m_ratio = 0.0;
119     int    m_lpi   = 0;
120 };
121
122 struct FluidDownscaleMapper : public FluidMapper
123 {
124     virtual int firstWindow(int outCoord, int lpi) const override;
125     virtual std::pair<int,int> linesReadAndNextWindow(int outCoord, int lpi) const override;
126     using FluidMapper::FluidMapper;
127 };
128
129 struct FluidUpscaleMapper : public FluidMapper
130 {
131     virtual int firstWindow(int outCoord, int lpi) const override;
132     virtual std::pair<int,int> linesReadAndNextWindow(int outCoord, int lpi) const override;
133     FluidUpscaleMapper(double ratio, int lpi, int inHeight) : FluidMapper(ratio, lpi), m_inHeight(inHeight) {}
134 private:
135     int m_inHeight = 0;
136 };
137
138 struct FluidFilterAgent : public FluidAgent
139 {
140 private:
141     virtual int firstWindow() const override;
142     virtual std::pair<int,int> linesReadAndnextWindow() const override;
143     virtual void setRatio(double) override { /* nothing */ }
144 public:
145     using FluidAgent::FluidAgent;
146 };
147
148 struct FluidResizeAgent : public FluidAgent
149 {
150 private:
151     virtual int firstWindow() const override;
152     virtual std::pair<int,int> linesReadAndnextWindow() const override;
153     virtual void setRatio(double ratio) override;
154
155     std::unique_ptr<FluidMapper> m_mapper;
156 public:
157     using FluidAgent::FluidAgent;
158 };
159 }} // namespace cv::gimpl
160
161 cv::gimpl::FluidAgent::FluidAgent(const ade::Graph &g, ade::NodeHandle nh)
162     : k(GConstFluidModel(g).metadata(nh).get<FluidUnit>().k)        // init(0)
163     , op_handle(nh)                                                 // init(1)
164     , op_name(GModel::ConstGraph(g).metadata(nh).get<Op>().k.name)  // init(2)
165 {
166     std::set<int> out_w;
167     std::set<int> out_h;
168     GModel::ConstGraph cm(g);
169     for (auto out_data : nh->outNodes())
170     {
171         const auto  &d      = cm.metadata(out_data).get<Data>();
172         cv::GMatDesc d_meta = cv::util::get<cv::GMatDesc>(d.meta);
173         out_w.insert(d_meta.size.width);
174         out_h.insert(d_meta.size.height);
175     }
176
177     // Different output sizes are not supported
178     GAPI_Assert(out_w.size() == 1 && out_h.size() == 1);
179 }
180
181 void cv::gimpl::FluidAgent::reset()
182 {
183     m_producedLines = 0;
184
185     auto lines = firstWindow();
186     for (auto &v : in_views)
187     {
188         if (v)
189         {
190             v.priv().reset(lines);
191         }
192     }
193 }
194
195 namespace {
196 static int calcGcd (int n1, int n2)
197 {
198     return (n2 == 0) ? n1 : calcGcd (n2, n1 % n2);
199 }
200
201 // This is an empiric formula and this is not 100% guaranteed
202 // that it produces correct results in all possible cases
203 // FIXME:
204 // prove correctness or switch to some trusted method
205 //
206 // When performing resize input/output pixels form a cyclic
207 // pattern where inH/gcd input pixels are mapped to outH/gcd
208 // output pixels (pattern repeats gcd times).
209 //
210 // Output pixel can partually cover some of the input pixels.
211 // There are 3 possible cases:
212 //
213 // :___ ___:    :___ _:_ ___:    :___ __: ___ :__ ___:
214 // |___|___|    |___|_:_|___|    |___|__:|___|:__|___|
215 // :       :    :     :     :    :      :     :      :
216 //
217 // 1) No partial coverage, max window = scaleFactor;
218 // 2) Partial coverage occurs on the one side of the output pixel,
219 //    max window = scaleFactor + 1;
220 // 3) Partial coverage occurs at both sides of the output pixel,
221 //    max window = scaleFactor + 2;
222 //
223 // Type of the coverage is determined by remainder of
224 // inPeriodH/outPeriodH division, but it's an heuristic
225 // (howbeit didn't found the proof of the opposite so far).
226
227 static int calcResizeWindow(int inH, int outH)
228 {
229     GAPI_Assert(inH >= outH);
230     auto gcd = calcGcd(inH, outH);
231     int  inPeriodH =  inH/gcd;
232     int outPeriodH = outH/gcd;
233     int scaleFactor = inPeriodH / outPeriodH;
234
235     switch ((inPeriodH) % (outPeriodH))
236     {
237     case 0:  return scaleFactor;     break;
238     case 1:  return scaleFactor + 1; break;
239     default: return scaleFactor + 2;
240     }
241 }
242
243 static int maxLineConsumption(const cv::GFluidKernel& k, int inH, int outH, int lpi)
244 {
245     switch (k.m_kind)
246     {
247     case cv::GFluidKernel::Kind::Filter: return k.m_window + lpi - 1; break;
248     case cv::GFluidKernel::Kind::Resize:
249     {
250         if  (inH >= outH)
251         {
252             // FIXME:
253             // This is a suboptimal value, can be reduced
254             return calcResizeWindow(inH, outH) * lpi;
255         }
256         else
257         {
258             // FIXME:
259             // This is a suboptimal value, can be reduced
260             return (inH == 1) ? 1 : 2 + lpi - 1;
261         }
262     } break;
263     default: GAPI_Assert(false); return 0;
264     }
265 }
266
267 static int borderSize(const cv::GFluidKernel& k)
268 {
269     switch (k.m_kind)
270     {
271     case cv::GFluidKernel::Kind::Filter: return (k.m_window - 1) / 2; break;
272     // Resize never reads from border pixels
273     case cv::GFluidKernel::Kind::Resize: return 0; break;
274     default: GAPI_Assert(false); return 0;
275     }
276 }
277
278 inline double inCoord(int outIdx, double ratio)
279 {
280     return outIdx * ratio;
281 }
282
283 inline int windowStart(int outIdx, double ratio)
284 {
285     return static_cast<int>(inCoord(outIdx, ratio) + 1e-3);
286 }
287
288 inline int windowEnd(int outIdx, double ratio)
289 {
290     return static_cast<int>(std::ceil(inCoord(outIdx + 1, ratio) - 1e-3));
291 }
292
293 inline double inCoordUpscale(int outCoord, double ratio)
294 {
295     // Calculate the projection of output pixel's center
296     return (outCoord + 0.5) * ratio - 0.5;
297 }
298
299 inline int upscaleWindowStart(int outCoord, double ratio)
300 {
301     int start = static_cast<int>(inCoordUpscale(outCoord, ratio));
302     GAPI_DbgAssert(start >= 0);
303     return start;
304 }
305
306 inline int upscaleWindowEnd(int outCoord, double ratio, int inSz)
307 {
308     int end = static_cast<int>(std::ceil(inCoordUpscale(outCoord, ratio)) + 1);
309     if (end > inSz)
310     {
311         end = inSz;
312     }
313     return end;
314 }
315 } // anonymous namespace
316
317 int cv::gimpl::FluidDownscaleMapper::firstWindow(int outCoord, int lpi) const
318 {
319     return windowEnd(outCoord + lpi - 1, m_ratio) - windowStart(outCoord, m_ratio);
320 }
321
322 std::pair<int,int> cv::gimpl::FluidDownscaleMapper::linesReadAndNextWindow(int outCoord, int lpi) const
323 {
324     auto nextStartIdx = outCoord + 1 + m_lpi - 1;
325     auto nextEndIdx   = nextStartIdx + lpi - 1;
326
327     auto currStart = windowStart(outCoord, m_ratio);
328     auto nextStart = windowStart(nextStartIdx, m_ratio);
329     auto nextEnd   = windowEnd(nextEndIdx, m_ratio);
330
331     auto lines_read = nextStart - currStart;
332     auto next_window = nextEnd - nextStart;
333
334     return std::make_pair(lines_read, next_window);
335 }
336
337 int cv::gimpl::FluidUpscaleMapper::firstWindow(int outCoord, int lpi) const
338 {
339     return upscaleWindowEnd(outCoord + lpi - 1, m_ratio, m_inHeight) - upscaleWindowStart(outCoord, m_ratio);
340 }
341
342 std::pair<int,int> cv::gimpl::FluidUpscaleMapper::linesReadAndNextWindow(int outCoord, int lpi) const
343 {
344     auto nextStartIdx = outCoord + 1 + m_lpi - 1;
345     auto nextEndIdx   = nextStartIdx + lpi - 1;
346
347     auto currStart = upscaleWindowStart(outCoord, m_ratio);
348     auto nextStart = upscaleWindowStart(nextStartIdx, m_ratio);
349     auto nextEnd   = upscaleWindowEnd(nextEndIdx, m_ratio, m_inHeight);
350
351     auto lines_read = nextStart - currStart;
352     auto next_window = nextEnd - nextStart;
353
354     return std::make_pair(lines_read, next_window);
355 }
356
357 int cv::gimpl::FluidFilterAgent::firstWindow() const
358 {
359     return k.m_window + k.m_lpi - 1;
360 }
361
362 std::pair<int,int> cv::gimpl::FluidFilterAgent::linesReadAndnextWindow() const
363 {
364     int lpi = std::min(k.m_lpi, m_outputLines - m_producedLines - k.m_lpi);
365     return std::make_pair(k.m_lpi, k.m_window - 1 + lpi);
366 }
367
368 int cv::gimpl::FluidResizeAgent::firstWindow() const
369 {
370     auto outIdx = out_buffers[0]->priv().y();
371     auto lpi = std::min(m_outputLines - m_producedLines, k.m_lpi);
372     return m_mapper->firstWindow(outIdx, lpi);
373 }
374
375 std::pair<int,int> cv::gimpl::FluidResizeAgent::linesReadAndnextWindow() const
376 {
377     auto outIdx = out_buffers[0]->priv().y();
378     auto lpi = std::min(m_outputLines - m_producedLines - k.m_lpi, k.m_lpi);
379     return m_mapper->linesReadAndNextWindow(outIdx, lpi);
380 }
381
382 void cv::gimpl::FluidResizeAgent::setRatio(double ratio)
383 {
384     if (ratio >= 1.0)
385     {
386         m_mapper.reset(new FluidDownscaleMapper(ratio, k.m_lpi));
387     }
388     else
389     {
390         m_mapper.reset(new FluidUpscaleMapper(ratio, k.m_lpi, in_views[0].meta().size.height));
391     }
392 }
393
394 bool cv::gimpl::FluidAgent::canRead() const
395 {
396     // An agent can work if every input buffer have enough data to start
397     for (const auto& in_view : in_views)
398     {
399         if (in_view)
400         {
401             if (!in_view.ready())
402                 return false;
403         }
404     }
405     return true;
406 }
407
408 bool cv::gimpl::FluidAgent::canWrite() const
409 {
410     // An agent can work if there is space to write in its output
411     // allocated buffers
412     GAPI_DbgAssert(!out_buffers.empty());
413     auto out_begin = out_buffers.begin();
414     auto out_end   = out_buffers.end();
415     if (k.m_scratch) out_end--;
416     for (auto it = out_begin; it != out_end; ++it)
417     {
418         if ((*it)->priv().full())
419         {
420             return false;
421         }
422     }
423     return true;
424 }
425
426 bool cv::gimpl::FluidAgent::canWork() const
427 {
428     return canRead() && canWrite();
429 }
430
431 void cv::gimpl::FluidAgent::doWork()
432 {
433     GAPI_DbgAssert(m_outputLines > m_producedLines);
434     for (auto& in_view : in_views)
435     {
436         if (in_view) in_view.priv().prepareToRead();
437     }
438
439     k.m_f(in_args, out_buffers);
440
441     for (auto& in_view : in_views)
442     {
443         if (in_view)
444         {
445             auto pair = linesReadAndnextWindow();
446             in_view.priv().readDone(pair.first, pair.second);
447         };
448     }
449
450     for (auto out_buf : out_buffers)
451     {
452         out_buf->priv().writeDone();
453         // FIXME WARNING: Scratch buffers rotated here too!
454     }
455
456     m_producedLines += k.m_lpi;
457 }
458
459 bool cv::gimpl::FluidAgent::done() const
460 {
461     // m_producedLines is a multiple of LPI, while original
462     // height may be not.
463     return m_producedLines >= m_outputLines;
464 }
465
466 void cv::gimpl::FluidAgent::debug(std::ostream &os)
467 {
468     os << "Fluid Agent " << std::hex << this
469        << " (" << op_name << ") --"
470        << " canWork=" << std::boolalpha << canWork()
471        << " canRead=" << std::boolalpha << canRead()
472        << " canWrite=" << std::boolalpha << canWrite()
473        << " done="    << done()
474        << " lines="   << std::dec << m_producedLines << "/" << m_outputLines
475        << " {{\n";
476     for (auto out_buf : out_buffers)
477     {
478         out_buf->debug(os);
479     }
480     std::cout << "}}" << std::endl;
481 }
482
483 // GCPUExcecutable implementation //////////////////////////////////////////////
484
485 void cv::gimpl::GFluidExecutable::initBufferRois(std::vector<int>& readStarts,
486                                                  std::vector<cv::gapi::own::Rect>& rois,
487                                                  const std::vector<cv::gapi::own::Rect>& out_rois)
488 {
489     GConstFluidModel fg(m_g);
490     auto proto = m_gm.metadata().get<Protocol>();
491     std::stack<ade::NodeHandle> nodesToVisit;
492
493     // FIXME?
494     // There is possible case when user pass the vector full of default Rect{}-s,
495     // Can be diagnosed and handled appropriately
496     if (proto.outputs.size() != out_rois.size())
497     {
498         GAPI_Assert(out_rois.size() == 0);
499         // No inference required, buffers will obtain roi from meta
500         return;
501     }
502
503     // First, initialize rois for output nodes, add them to traversal stack
504     for (const auto& it : ade::util::indexed(proto.out_nhs))
505     {
506         const auto idx = ade::util::index(it);
507         const auto nh  = ade::util::value(it);
508
509         const auto &d  = m_gm.metadata(nh).get<Data>();
510
511         // This is not our output
512         if (m_id_map.count(d.rc) == 0)
513         {
514             continue;
515         }
516
517         if (d.shape == GShape::GMAT)
518         {
519             auto desc = util::get<GMatDesc>(d.meta);
520             auto id = m_id_map.at(d.rc);
521             readStarts[id] = 0;
522
523             if (out_rois[idx] == gapi::own::Rect{})
524             {
525                 rois[id] = gapi::own::Rect{ 0, 0, desc.size.width, desc.size.height };
526             }
527             else
528             {
529                 // Only slices are supported at the moment
530                 GAPI_Assert(out_rois[idx].x == 0);
531                 GAPI_Assert(out_rois[idx].width == desc.size.width);
532                 rois[id] = out_rois[idx];
533             }
534
535             nodesToVisit.push(nh);
536         }
537     }
538
539     // Perform a wide search from each of the output nodes
540     // And extend roi of buffers by border_size
541     // Each node can be visited multiple times
542     // (if node has been already visited, the check that inferred rois are the same is performed)
543     while (!nodesToVisit.empty())
544     {
545         const auto startNode = nodesToVisit.top();
546         nodesToVisit.pop();
547
548         if (!startNode->inNodes().empty())
549         {
550             GAPI_Assert(startNode->inNodes().size() == 1);
551             const auto& oh = startNode->inNodes().front();
552
553             const auto& data = m_gm.metadata(startNode).get<Data>();
554             // only GMats participate in the process so it's valid to obtain GMatDesc
555             const auto& meta = util::get<GMatDesc>(data.meta);
556
557             for (const auto& inNode : oh->inNodes())
558             {
559                 const auto& in_data = m_gm.metadata(inNode).get<Data>();
560
561                 if (in_data.shape == GShape::GMAT && fg.metadata(inNode).contains<FluidData>())
562                 {
563                     const auto& in_meta = util::get<GMatDesc>(in_data.meta);
564                     const auto& fd = fg.metadata(inNode).get<FluidData>();
565
566                     auto adjFilterRoi = [](cv::gapi::own::Rect produced, int b, int max_height) {
567                         // Extend with border roi which should be produced, crop to logical image size
568                         cv::gapi::own::Rect roi = {produced.x, produced.y - b, produced.width, produced.height + 2*b};
569                         cv::gapi::own::Rect fullImg{ 0, 0, produced.width, max_height };
570                         return roi & fullImg;
571                     };
572
573                     auto adjResizeRoi = [](cv::gapi::own::Rect produced, cv::gapi::own::Size inSz, cv::gapi::own::Size outSz) {
574                         auto map = [](int outCoord, int producedSz, int inSize, int outSize) {
575                             double ratio = (double)inSize / outSize;
576                             int w0 = 0, w1 = 0;
577                             if (ratio >= 1.0)
578                             {
579                                 w0 = windowStart(outCoord, ratio);
580                                 w1 = windowEnd  (outCoord + producedSz - 1, ratio);
581                             }
582                             else
583                             {
584                                 w0 = upscaleWindowStart(outCoord, ratio);
585                                 w1 = upscaleWindowEnd(outCoord + producedSz - 1, ratio, inSize);
586                             }
587                             return std::make_pair(w0, w1);
588                         };
589
590                         auto mapY = map(produced.y, produced.height, inSz.height, outSz.height);
591                         auto y0 = mapY.first;
592                         auto y1 = mapY.second;
593
594                         auto mapX = map(produced.x, produced.width, inSz.width, outSz.width);
595                         auto x0 = mapX.first;
596                         auto x1 = mapX.second;
597
598                         cv::gapi::own::Rect roi = {x0, y0, x1 - x0, y1 - y0};
599                         return roi;
600                     };
601
602                     cv::gapi::own::Rect produced = rois[m_id_map.at(data.rc)];
603
604                     cv::gapi::own::Rect resized;
605                     switch (fg.metadata(oh).get<FluidUnit>().k.m_kind)
606                     {
607                     case GFluidKernel::Kind::Filter: resized = produced; break;
608                     case GFluidKernel::Kind::Resize: resized = adjResizeRoi(produced, in_meta.size, meta.size); break;
609                     default: GAPI_Assert(false);
610                     }
611
612                     int readStart = resized.y;
613                     cv::gapi::own::Rect roi = adjFilterRoi(resized, fd.border_size, in_meta.size.height);
614
615                     auto in_id = m_id_map.at(in_data.rc);
616                     if (rois[in_id] == cv::gapi::own::Rect{})
617                     {
618                         readStarts[in_id] = readStart;
619                         rois[in_id] = roi;
620                         // Continue traverse on internal (w.r.t Island) data nodes only.
621                         if (fd.internal) nodesToVisit.push(inNode);
622                     }
623                     else
624                     {
625                         GAPI_Assert(readStarts[in_id] == readStart);
626                         GAPI_Assert(rois[in_id] == roi);
627                     }
628                 } // if (in_data.shape == GShape::GMAT)
629             } // for (const auto& inNode : oh->inNodes())
630         } // if (!startNode->inNodes().empty())
631     } // while (!nodesToVisit.empty())
632 }
633
634 cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
635                                               const std::vector<ade::NodeHandle> &nodes,
636                                               const std::vector<cv::gapi::own::Rect> &outputRois)
637     : m_g(g), m_gm(m_g)
638 {
639     GConstFluidModel fg(m_g);
640
641     // Initialize vector of data buffers, build list of operations
642     // FIXME: There _must_ be a better way to [query] count number of DATA nodes
643     std::size_t mat_count = 0;
644     std::size_t last_agent = 0;
645
646     auto grab_mat_nh = [&](ade::NodeHandle nh) {
647         auto rc = m_gm.metadata(nh).get<Data>().rc;
648         if (m_id_map.count(rc) == 0)
649         {
650             m_all_gmat_ids[mat_count] = nh;
651             m_id_map[rc] = mat_count++;
652         }
653     };
654
655     for (const auto &nh : nodes)
656     {
657         switch (m_gm.metadata(nh).get<NodeType>().t)
658         {
659         case NodeType::DATA:
660             if (m_gm.metadata(nh).get<Data>().shape == GShape::GMAT)
661                 grab_mat_nh(nh);
662             break;
663
664         case NodeType::OP:
665         {
666             const auto& fu = fg.metadata(nh).get<FluidUnit>();
667             switch (fu.k.m_kind)
668             {
669             case GFluidKernel::Kind::Filter: m_agents.emplace_back(new FluidFilterAgent(m_g, nh)); break;
670             case GFluidKernel::Kind::Resize: m_agents.emplace_back(new FluidResizeAgent(m_g, nh)); break;
671             default: GAPI_Assert(false);
672             }
673             // NB.: in_buffer_ids size is equal to Arguments size, not Edges size!!!
674             m_agents.back()->in_buffer_ids.resize(m_gm.metadata(nh).get<Op>().args.size(), -1);
675             for (auto eh : nh->inEdges())
676             {
677                 // FIXME Only GMats are currently supported (which can be represented
678                 // as fluid buffers
679                 if (m_gm.metadata(eh->srcNode()).get<Data>().shape == GShape::GMAT)
680                 {
681                     const auto in_port = m_gm.metadata(eh).get<Input>().port;
682                     const int  in_buf  = m_gm.metadata(eh->srcNode()).get<Data>().rc;
683
684                     m_agents.back()->in_buffer_ids[in_port] = in_buf;
685                     grab_mat_nh(eh->srcNode());
686                 }
687             }
688             // FIXME: Assumption that all operation outputs MUST be connected
689             m_agents.back()->out_buffer_ids.resize(nh->outEdges().size(), -1);
690             for (auto eh : nh->outEdges())
691             {
692                 const auto& data = m_gm.metadata(eh->dstNode()).get<Data>();
693                 const auto out_port = m_gm.metadata(eh).get<Output>().port;
694                 const int  out_buf  = data.rc;
695
696                 m_agents.back()->out_buffer_ids[out_port] = out_buf;
697                 if (data.shape == GShape::GMAT) grab_mat_nh(eh->dstNode());
698             }
699             if (fu.k.m_scratch)
700                 m_scratch_users.push_back(last_agent);
701             last_agent++;
702             break;
703         }
704         default: GAPI_Assert(false);
705         }
706     }
707
708     // Check that IDs form a continiuos set (important for further indexing)
709     GAPI_Assert(m_id_map.size() >  0);
710     GAPI_Assert(m_id_map.size() == static_cast<size_t>(mat_count));
711
712     // Actually initialize Fluid buffers
713     GAPI_LOG_INFO(NULL, "Initializing " << mat_count << " fluid buffer(s)" << std::endl);
714     m_num_int_buffers = mat_count;
715     const std::size_t num_scratch = m_scratch_users.size();
716     m_buffers.resize(m_num_int_buffers + num_scratch);
717
718     // After buffers are allocated, repack: ...
719     for (auto &agent : m_agents)
720     {
721         // a. Agent input parameters with View pointers (creating Views btw)
722         const auto &op = m_gm.metadata(agent->op_handle).get<Op>();
723         const auto &fu =   fg.metadata(agent->op_handle).get<FluidUnit>();
724         agent->in_args.resize(op.args.size());
725         agent->in_views.resize(op.args.size());
726         for (auto it : ade::util::indexed(ade::util::toRange(agent->in_buffer_ids)))
727         {
728             auto in_idx  = ade::util::index(it);
729             auto buf_idx = ade::util::value(it);
730
731             if (buf_idx >= 0)
732             {
733                 // IF there is input buffer, register a view (every unique
734                 // reader has its own), and store it in agent Args
735                 gapi::fluid::Buffer &buffer = m_buffers.at(m_id_map.at(buf_idx));
736
737                 auto inEdge = GModel::getInEdgeByPort(m_g, agent->op_handle, in_idx);
738                 auto ownStorage = fg.metadata(inEdge).get<FluidUseOwnBorderBuffer>().use;
739
740                 gapi::fluid::View view = buffer.mkView(fu.border_size, ownStorage);
741                 // NB: It is safe to keep ptr as view lifetime is buffer lifetime
742                 agent->in_views[in_idx] = view;
743                 agent->in_args[in_idx]  = GArg(view);
744             }
745             else
746             {
747                 // Copy(FIXME!) original args as is
748                 agent->in_args[in_idx] = op.args[in_idx];
749             }
750         }
751
752         // b. Agent output parameters with Buffer pointers.
753         agent->out_buffers.resize(agent->op_handle->outEdges().size(), nullptr);
754         for (auto it : ade::util::indexed(ade::util::toRange(agent->out_buffer_ids)))
755         {
756             auto out_idx = ade::util::index(it);
757             auto buf_idx = m_id_map.at(ade::util::value(it));
758             agent->out_buffers.at(out_idx) = &m_buffers.at(buf_idx);
759         }
760     }
761
762     // After parameters are there, initialize scratch buffers
763     if (num_scratch)
764     {
765         GAPI_LOG_INFO(NULL, "Initializing " << num_scratch << " scratch buffer(s)" << std::endl);
766         std::size_t last_scratch_id = 0;
767
768         for (auto i : m_scratch_users)
769         {
770             auto &agent = m_agents.at(i);
771             GAPI_Assert(agent->k.m_scratch);
772             const std::size_t new_scratch_idx = m_num_int_buffers + last_scratch_id;
773             agent->out_buffers.emplace_back(&m_buffers[new_scratch_idx]);
774             last_scratch_id++;
775         }
776     }
777
778     makeReshape(outputRois);
779
780     std::size_t total_size = 0;
781     for (const auto &i : ade::util::indexed(m_buffers))
782     {
783         // Check that all internal and scratch buffers are allocated
784         const auto idx = ade::util::index(i);
785         const auto b   = ade::util::value(i);
786         if (idx >= m_num_int_buffers ||
787             fg.metadata(m_all_gmat_ids[idx]).get<FluidData>().internal == true)
788         {
789             GAPI_Assert(b.priv().size() > 0);
790         }
791
792         // Buffers which will be bound to real images may have size of 0 at this moment
793         // (There can be non-zero sized const border buffer allocated in such buffers)
794         total_size += b.priv().size();
795     }
796     GAPI_LOG_INFO(NULL, "Internal buffers: " << std::fixed << std::setprecision(2) << static_cast<float>(total_size)/1024 << " KB\n");
797 }
798
799 namespace
800 {
801     void resetFluidData(ade::Graph& graph)
802     {
803         using namespace cv::gimpl;
804         GModel::Graph g(graph);
805         GFluidModel fg(graph);
806         for (const auto node : g.nodes())
807         {
808             if (g.metadata(node).get<NodeType>().t == NodeType::DATA)
809             {
810                 auto& fd = fg.metadata(node).get<FluidData>();
811                 fd.latency         = 0;
812                 fd.skew            = 0;
813                 fd.max_consumption = 0;
814             }
815         }
816     }
817
818     void initFluidUnits(ade::Graph& graph)
819     {
820         using namespace cv::gimpl;
821         GModel::Graph g(graph);
822         GFluidModel fg(graph);
823
824         auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
825         for (auto node : sorted)
826         {
827             if (fg.metadata(node).contains<FluidUnit>())
828             {
829                 std::set<int> in_hs, out_ws, out_hs;
830
831                 for (const auto& in : node->inNodes())
832                 {
833                     const auto& d = g.metadata(in).get<Data>();
834                     if (d.shape == cv::GShape::GMAT)
835                     {
836                         const auto& meta = cv::util::get<cv::GMatDesc>(d.meta);
837                         in_hs.insert(meta.size.height);
838                     }
839                 }
840
841                 for (const auto& out : node->outNodes())
842                 {
843                     const auto& d = g.metadata(out).get<Data>();
844                     if (d.shape == cv::GShape::GMAT)
845                     {
846                         const auto& meta = cv::util::get<cv::GMatDesc>(d.meta);
847                         out_ws.insert(meta.size.width);
848                         out_hs.insert(meta.size.height);
849                     }
850                 }
851
852                 GAPI_Assert(in_hs.size() == 1 && out_ws.size() == 1 && out_hs.size() == 1);
853
854                 auto in_h  = *in_hs .cbegin();
855                 auto out_h = *out_hs.cbegin();
856
857                 auto &fu = fg.metadata(node).get<FluidUnit>();
858                 fu.ratio = (double)in_h / out_h;
859
860                 int line_consumption = maxLineConsumption(fu.k, in_h, out_h, fu.k.m_lpi);
861                 int border_size = borderSize(fu.k);
862
863                 fu.border_size = border_size;
864                 fu.line_consumption = line_consumption;
865
866                 GModel::log(g, node, "Line consumption: " + std::to_string(fu.line_consumption));
867                 GModel::log(g, node, "Border size: " + std::to_string(fu.border_size));
868             }
869         }
870     }
871
872     // FIXME!
873     // Split into initLineConsumption and initBorderSizes,
874     // call only consumption related stuff during reshape
875     void initLineConsumption(ade::Graph& graph)
876     {
877         using namespace cv::gimpl;
878         GModel::Graph g(graph);
879         GFluidModel fg(graph);
880
881         for (const auto &node : g.nodes())
882         {
883             if (fg.metadata(node).contains<FluidUnit>())
884             {
885                 const auto &fu = fg.metadata(node).get<FluidUnit>();
886
887                 for (const auto &in_data_node : node->inNodes())
888                 {
889                     auto &fd = fg.metadata(in_data_node).get<FluidData>();
890
891                     // Update (not Set) fields here since a single data node may be
892                     // accessed by multiple consumers
893                     fd.max_consumption = std::max(fu.line_consumption, fd.max_consumption);
894                     fd.border_size     = std::max(fu.border_size, fd.border_size);
895
896                     GModel::log(g, in_data_node, "Line consumption: " + std::to_string(fd.max_consumption)
897                                 + " (upd by " + std::to_string(fu.line_consumption) + ")", node);
898                     GModel::log(g, in_data_node, "Border size: " + std::to_string(fd.border_size), node);
899                 }
900             }
901         }
902     }
903
904     void calcLatency(ade::Graph& graph)
905     {
906         using namespace cv::gimpl;
907         GModel::Graph g(graph);
908         GFluidModel fg(graph);
909
910         auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
911         for (const auto &node : sorted)
912         {
913             if (fg.metadata(node).contains<FluidUnit>())
914             {
915                 const auto &fu = fg.metadata(node).get<FluidUnit>();
916
917                 const int own_latency = fu.line_consumption - fu.border_size;
918                 GModel::log(g, node, "LPI: " + std::to_string(fu.k.m_lpi));
919
920                 // Output latency is max(input_latency) + own_latency
921                 int in_latency = 0;
922                 for (const auto &in_data_node : node->inNodes())
923                 {
924                     // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA)
925                     in_latency = std::max(in_latency, fg.metadata(in_data_node).get<FluidData>().latency);
926                 }
927                 const int out_latency = in_latency + own_latency;
928
929                 for (const auto &out_data_node : node->outNodes())
930                 {
931                     // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA)
932                     auto &fd     = fg.metadata(out_data_node).get<FluidData>();
933                     // If fluid node is external, it will be bound to a real image without
934                     // fluid buffer allocation, so set its latency to 0 not to confuse later latency propagation.
935                     // Latency is used in fluid buffer allocation process and is not used by the scheduler
936                     // so latency doesn't affect the execution and setting it to 0 is legal
937                     fd.latency   = fd.internal ? out_latency : 0;
938                     fd.lpi_write = fu.k.m_lpi;
939                     GModel::log(g, out_data_node, "Latency: " + std::to_string(fd.latency));
940                 }
941             }
942         }
943     }
944
945     void calcSkew(ade::Graph& graph)
946     {
947         using namespace cv::gimpl;
948         GModel::Graph g(graph);
949         GFluidModel fg(graph);
950
951         auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
952         for (const auto &node : sorted)
953         {
954             if (fg.metadata(node).contains<FluidUnit>())
955             {
956                 int max_latency = 0;
957                 for (const auto &in_data_node : node->inNodes())
958                 {
959                     // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA)
960                     max_latency = std::max(max_latency, fg.metadata(in_data_node).get<FluidData>().latency);
961                 }
962                 for (const auto &in_data_node : node->inNodes())
963                 {
964                     // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA)
965                     auto &fd = fg.metadata(in_data_node).get<FluidData>();
966
967                     // Update (not Set) fields here since a single data node may be
968                     // accessed by multiple consumers
969                     fd.skew = std::max(fd.skew, max_latency - fd.latency);
970
971                     GModel::log(g, in_data_node, "Skew: " + std::to_string(fd.skew), node);
972                 }
973             }
974         }
975     }
976 }
977
978 void cv::gimpl::GFluidExecutable::makeReshape(const std::vector<gapi::own::Rect> &out_rois)
979 {
980     GConstFluidModel fg(m_g);
981
982     // Calculate rois for each fluid buffer
983     std::vector<int> readStarts(m_num_int_buffers);
984     std::vector<cv::gapi::own::Rect> rois(m_num_int_buffers);
985     initBufferRois(readStarts, rois, out_rois);
986
987     // NB: Allocate ALL buffer object at once, and avoid any further reallocations
988     // (since raw pointers-to-elements are taken)
989     for (const auto &it : m_all_gmat_ids)
990     {
991         auto id = it.first;
992         auto nh = it.second;
993         const auto & d  = m_gm.metadata(nh).get<Data>();
994         const auto &fd  = fg.metadata(nh).get<FluidData>();
995         const auto meta = cv::util::get<GMatDesc>(d.meta);
996
997         m_buffers[id].priv().init(meta, fd.lpi_write, readStarts[id], rois[id]);
998
999         // TODO:
1000         // Introduce Storage::INTERNAL_GRAPH and Storage::INTERNAL_ISLAND?
1001         if (fd.internal == true)
1002         {
1003             m_buffers[id].priv().allocate(fd.border, fd.border_size, fd.max_consumption, fd.skew);
1004             std::stringstream stream;
1005             m_buffers[id].debug(stream);
1006             GAPI_LOG_INFO(NULL, stream.str());
1007         }
1008     }
1009
1010     // Allocate views, initialize agents
1011     for (auto &agent : m_agents)
1012     {
1013         const auto &fu = fg.metadata(agent->op_handle).get<FluidUnit>();
1014         for (auto it : ade::util::indexed(ade::util::toRange(agent->in_buffer_ids)))
1015         {
1016             auto in_idx  = ade::util::index(it);
1017             auto buf_idx = ade::util::value(it);
1018
1019             if (buf_idx >= 0)
1020             {
1021                 agent->in_views[in_idx].priv().allocate(fu.line_consumption, fu.border);
1022             }
1023         }
1024
1025         agent->setRatio(fu.ratio);
1026         agent->m_outputLines = agent->out_buffers.front()->priv().outputLines();
1027     }
1028
1029     // Initialize scratch buffers
1030     if (m_scratch_users.size())
1031     {
1032         for (auto i : m_scratch_users)
1033         {
1034             auto &agent = m_agents.at(i);
1035             GAPI_Assert(agent->k.m_scratch);
1036
1037             // Trigger Scratch buffer initialization method
1038             agent->k.m_is(GModel::collectInputMeta(m_gm, agent->op_handle), agent->in_args, *agent->out_buffers.back());
1039             std::stringstream stream;
1040             agent->out_buffers.back()->debug(stream);
1041             GAPI_LOG_INFO(NULL, stream.str());
1042         }
1043     }
1044
1045     // FIXME: calculate the size (lpi * ..)
1046     m_script.clear();
1047     m_script.reserve(10000);
1048 }
1049
1050 void cv::gimpl::GFluidExecutable::reshape(ade::Graph &g, const GCompileArgs &args)
1051 {
1052     // FIXME: Probably this needs to be integrated into common pass re-run routine
1053     // Backends may want to mark with passes to re-run on reshape and framework could
1054     // do it system-wide (without need in every backend handling reshape() directly).
1055     // This design needs to be analyzed for implementation.
1056     resetFluidData(g);
1057     initFluidUnits(g);
1058     initLineConsumption(g);
1059     calcLatency(g);
1060     calcSkew(g);
1061     const auto out_rois = cv::gimpl::getCompileArg<cv::GFluidOutputRois>(args).value_or(cv::GFluidOutputRois());
1062     makeReshape(out_rois.rois);
1063 }
1064
1065 // FIXME: Document what it does
1066 void cv::gimpl::GFluidExecutable::bindInArg(const cv::gimpl::RcDesc &rc, const GRunArg &arg)
1067 {
1068     switch (rc.shape)
1069     {
1070     case GShape::GMAT:    m_buffers[m_id_map.at(rc.id)].priv().bindTo(util::get<cv::gapi::own::Mat>(arg), true); break;
1071     case GShape::GSCALAR: m_res.slot<cv::gapi::own::Scalar>()[rc.id] = util::get<cv::gapi::own::Scalar>(arg); break;
1072     default: util::throw_error(std::logic_error("Unsupported GShape type"));
1073     }
1074 }
1075
1076 void cv::gimpl::GFluidExecutable::bindOutArg(const cv::gimpl::RcDesc &rc, const GRunArgP &arg)
1077 {
1078     // Only GMat is supported as return type
1079     switch (rc.shape)
1080     {
1081     case GShape::GMAT:
1082         {
1083             cv::GMatDesc desc = m_buffers[m_id_map.at(rc.id)].meta();
1084             auto      &outMat = *util::get<cv::gapi::own::Mat*>(arg);
1085             GAPI_Assert(outMat.data != nullptr);
1086             GAPI_Assert(descr_of(outMat) == desc && "Output argument was not preallocated as it should be ?");
1087             m_buffers[m_id_map.at(rc.id)].priv().bindTo(outMat, false);
1088             break;
1089         }
1090     default: util::throw_error(std::logic_error("Unsupported return GShape type"));
1091     }
1092 }
1093
1094 void cv::gimpl::GFluidExecutable::packArg(cv::GArg &in_arg, const cv::GArg &op_arg)
1095 {
1096     GAPI_Assert(op_arg.kind != cv::detail::ArgKind::GMAT
1097            && op_arg.kind != cv::detail::ArgKind::GSCALAR);
1098
1099     if (op_arg.kind == cv::detail::ArgKind::GOBJREF)
1100     {
1101         const cv::gimpl::RcDesc &ref = op_arg.get<cv::gimpl::RcDesc>();
1102         if (ref.shape == GShape::GSCALAR)
1103         {
1104             in_arg = GArg(m_res.slot<cv::gapi::own::Scalar>()[ref.id]);
1105         }
1106     }
1107 }
1108
1109 void cv::gimpl::GFluidExecutable::run(std::vector<InObj>  &&input_objs,
1110                                       std::vector<OutObj> &&output_objs)
1111 {
1112     // Bind input buffers from parameters
1113     for (auto& it : input_objs)  bindInArg(it.first, it.second);
1114     for (auto& it : output_objs) bindOutArg(it.first, it.second);
1115
1116     // Reset Buffers and Agents state before we go
1117     for (auto &buffer : m_buffers)
1118         buffer.priv().reset();
1119
1120     for (auto &agent : m_agents)
1121     {
1122         agent->reset();
1123         // Pass input cv::Scalar's to agent argument
1124         const auto& op = m_gm.metadata(agent->op_handle).get<Op>();
1125         for (const auto& it : ade::util::indexed(op.args))
1126         {
1127             const auto& arg = ade::util::value(it);
1128             packArg(agent->in_args[ade::util::index(it)], arg);
1129         }
1130     }
1131
1132     // Explicitly reset Scratch buffers, if any
1133     for (auto scratch_i : m_scratch_users)
1134     {
1135         auto &agent = m_agents[scratch_i];
1136         GAPI_DbgAssert(agent->k.m_scratch);
1137         agent->k.m_rs(*agent->out_buffers.back());
1138     }
1139
1140     // Now start executing our stuff!
1141     // Fluid execution is:
1142     // - run through list of Agents from Left to Right
1143     // - for every Agent:
1144     //   - if all input Buffers have enough data to fulfill
1145     //     Agent's window - trigger Agent
1146     //     - on trigger, Agent takes all input lines from input buffers
1147     //       and produces a single output line
1148     //     - once Agent finishes, input buffers get "readDone()",
1149     //       and output buffers get "writeDone()"
1150     //   - if there's not enough data, Agent is skipped
1151     // Yes, THAT easy!
1152
1153     if (m_script.empty())
1154     {
1155         bool complete = true;
1156         do {
1157             complete = true;
1158             bool work_done=false;
1159             for (auto &agent : m_agents)
1160             {
1161                 // agent->debug(std::cout);
1162                 if (!agent->done())
1163                 {
1164                     if (agent->canWork())
1165                     {
1166                         agent->doWork(); work_done=true;
1167                         m_script.push_back(agent.get());
1168                     }
1169                     if (!agent->done())   complete = false;
1170                 }
1171             }
1172             GAPI_Assert(work_done || complete);
1173         } while (!complete); // FIXME: number of iterations can be calculated statically
1174     }
1175     else
1176     {
1177         for (auto &agent : m_script)
1178         {
1179             agent->doWork();
1180         }
1181     }
1182 }
1183
1184 // FIXME: these passes operate on graph global level!!!
1185 // Need to fix this for heterogeneous (island-based) processing
1186 void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
1187 {
1188     using namespace cv::gimpl;
1189
1190     // FIXME: all passes were moved to "exec" stage since Fluid
1191     // should check Islands configuration first (which is now quite
1192     // limited), and only then continue with all other passes.
1193     //
1194     // The passes/stages API must be streamlined!
1195     ectx.addPass("exec", "init_fluid_data", [](ade::passes::PassContext &ctx)
1196     {
1197         GModel::Graph g(ctx.graph);
1198         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
1199             return;
1200
1201         auto isl_graph = g.metadata().get<IslandModel>().model;
1202         GIslandModel::Graph gim(*isl_graph);
1203
1204         GFluidModel fg(ctx.graph);
1205
1206         const auto setFluidData = [&](ade::NodeHandle nh, bool internal) {
1207             FluidData fd;
1208             fd.internal = internal;
1209             fg.metadata(nh).set(fd);
1210         };
1211
1212         for (const auto& nh : gim.nodes())
1213         {
1214             switch (gim.metadata(nh).get<NodeKind>().k)
1215             {
1216             case NodeKind::ISLAND:
1217             {
1218                 const auto isl = gim.metadata(nh).get<FusedIsland>().object;
1219                 if (isl->backend() == cv::gapi::fluid::backend())
1220                 {
1221                     // Add FluidData to all data nodes inside island,
1222                     // set internal = true if node is not a slot in terms of higher-level GIslandModel
1223                     for (const auto node : isl->contents())
1224                     {
1225                         if (g.metadata(node).get<NodeType>().t == NodeType::DATA &&
1226                             !fg.metadata(node).contains<FluidData>())
1227                             setFluidData(node, true);
1228                     }
1229                 } // if (fluid backend)
1230             } break; // case::ISLAND
1231             case NodeKind::SLOT:
1232             {
1233                 // add FluidData to slot if it's read/written by fluid
1234                 // regardless if it is one fluid island (both writing to and reading from this object)
1235                 // or two distinct islands (both fluid)
1236                 auto isFluidIsland = [&](const ade::NodeHandle& node) {
1237                     const auto isl = gim.metadata(node).get<FusedIsland>().object;
1238                     return isl->backend() == cv::gapi::fluid::backend();
1239                 };
1240
1241                 if (ade::util::any_of(ade::util::chain(nh->inNodes(), nh->outNodes()), isFluidIsland))
1242                 {
1243                     auto data_node = gim.metadata(nh).get<DataSlot>().original_data_node;
1244                     setFluidData(data_node, false);
1245                 }
1246             } break; // case::SLOT
1247             default: GAPI_Assert(false);
1248             } // switch
1249         } // for (gim.nodes())
1250     });
1251     // FIXME:
1252     // move to unpackKernel method
1253     // when https://gitlab-icv.inn.intel.com/G-API/g-api/merge_requests/66 is merged
1254     ectx.addPass("exec", "init_fluid_unit_borders", [](ade::passes::PassContext &ctx)
1255     {
1256         GModel::Graph g(ctx.graph);
1257         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
1258             return;
1259
1260         GFluidModel fg(ctx.graph);
1261
1262         auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
1263         for (auto node : sorted)
1264         {
1265             if (fg.metadata(node).contains<FluidUnit>())
1266             {
1267                 // FIXME: check that op has only one data node on input
1268                 auto &fu = fg.metadata(node).get<FluidUnit>();
1269                 const auto &op = g.metadata(node).get<Op>();
1270
1271                 // Trigger user-defined "getBorder" callback
1272                 fu.border = fu.k.m_b(GModel::collectInputMeta(fg, node), op.args);
1273             }
1274         }
1275     });
1276     ectx.addPass("exec", "init_fluid_units", [](ade::passes::PassContext &ctx)
1277     {
1278         GModel::Graph g(ctx.graph);
1279         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
1280             return;
1281
1282         initFluidUnits(ctx.graph);
1283     });
1284     ectx.addPass("exec", "init_line_consumption", [](ade::passes::PassContext &ctx)
1285     {
1286         GModel::Graph g(ctx.graph);
1287         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
1288             return;
1289
1290         initLineConsumption(ctx.graph);
1291     });
1292     ectx.addPass("exec", "calc_latency", [](ade::passes::PassContext &ctx)
1293     {
1294         GModel::Graph g(ctx.graph);
1295         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
1296             return;
1297
1298         calcLatency(ctx.graph);
1299     });
1300     ectx.addPass("exec", "calc_skew", [](ade::passes::PassContext &ctx)
1301     {
1302         GModel::Graph g(ctx.graph);
1303         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
1304             return;
1305
1306         calcSkew(ctx.graph);
1307     });
1308
1309     ectx.addPass("exec", "init_buffer_borders", [](ade::passes::PassContext &ctx)
1310     {
1311         GModel::Graph g(ctx.graph);
1312         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
1313             return;
1314
1315         GFluidModel fg(ctx.graph);
1316         auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
1317         for (auto node : sorted)
1318         {
1319             if (fg.metadata(node).contains<FluidData>())
1320             {
1321                 auto &fd = fg.metadata(node).get<FluidData>();
1322
1323                 // Assign border stuff to FluidData
1324
1325                 // In/out data nodes are bound to user data directly,
1326                 // so cannot be extended with a border
1327                 if (fd.internal == true)
1328                 {
1329                     // For now border of the buffer's storage is the border
1330                     // of the first reader whose border size is the same.
1331                     // FIXME: find more clever strategy of border picking
1332                     // (it can be a border which is common for majority of the
1333                     // readers, also we can calculate the number of lines which
1334                     // will be copied by views on each iteration and base our choice
1335                     // on this criteria)
1336                     auto readers = node->outNodes();
1337                     const auto &candidate = ade::util::find_if(readers, [&](ade::NodeHandle nh) {
1338                         return fg.metadata(nh).contains<FluidUnit>() &&
1339                                fg.metadata(nh).get<FluidUnit>().border_size == fd.border_size;
1340                     });
1341
1342                     GAPI_Assert(candidate != readers.end());
1343
1344                     const auto &fu = fg.metadata(*candidate).get<FluidUnit>();
1345                     fd.border = fu.border;
1346                 }
1347
1348                 if (fd.border)
1349                 {
1350                     GModel::log(g, node, "Border type: " + std::to_string(fd.border->type), node);
1351                 }
1352             }
1353         }
1354     });
1355     ectx.addPass("exec", "init_view_borders", [](ade::passes::PassContext &ctx)
1356     {
1357         GModel::Graph g(ctx.graph);
1358         if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
1359             return;
1360
1361         GFluidModel fg(ctx.graph);
1362         for (auto node : g.nodes())
1363         {
1364             if (fg.metadata(node).contains<FluidData>())
1365             {
1366                 auto &fd = fg.metadata(node).get<FluidData>();
1367                 for (auto out_edge : node->outEdges())
1368                 {
1369                     const auto dstNode = out_edge->dstNode();
1370                     if (fg.metadata(dstNode).contains<FluidUnit>())
1371                     {
1372                         const auto &fu = fg.metadata(dstNode).get<FluidUnit>();
1373
1374                         // There is no need in own storage for view if it's border is
1375                         // the same as the buffer's (view can have equal or smaller border
1376                         // size in this case)
1377                         if (fu.border_size == 0 ||
1378                                 (fu.border && fd.border && (*fu.border == *fd.border)))
1379                         {
1380                             GAPI_Assert(fu.border_size <= fd.border_size);
1381                             fg.metadata(out_edge).set(FluidUseOwnBorderBuffer{false});
1382                         }
1383                         else
1384                         {
1385                             fg.metadata(out_edge).set(FluidUseOwnBorderBuffer{true});
1386                             GModel::log(g, out_edge, "OwnBufferStorage: true");
1387                         }
1388                     }
1389                 }
1390             }
1391         }
1392     });
1393 }