Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / thirdparty / fluid / modules / gapi / src / backends / fluid / gfluidbuffer.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 <iomanip>   // hex, dec (debug)
11
12 #include "opencv2/gapi/own/convert.hpp"
13 #include "opencv2/gapi/own/types.hpp"
14
15 #include "opencv2/gapi/fluid/gfluidbuffer.hpp"
16 #include "backends/fluid/gfluidbuffer_priv.hpp"
17 #include "opencv2/gapi/opencv_includes.hpp"
18
19 #include "backends/fluid/gfluidutils.hpp" // saturate
20
21 namespace cv {
22 namespace gapi {
23 namespace fluid {
24 bool operator == (const fluid::Border& b1, const fluid::Border& b2)
25 {
26     return b1.type == b2.type && b1.value == b2.value;
27 }
28 } // namespace fluid
29
30 // Fluid BorderHandler implementation /////////////////////////////////////////////////
31
32 namespace {
33 template<typename T>
34 // Expected inputs:
35 // row - row buffer allocated with border in mind (have memory for both image and border pixels)
36 // length - size of the buffer with left and right borders included
37 void fillBorderReplicateRow(uint8_t* row, int length, int chan, int borderSize)
38 {
39     auto leftBorder  = reinterpret_cast<T*>(row);
40     auto rightBorder = leftBorder + (length - borderSize) * chan;
41     for (int b = 0; b < borderSize; b++)
42     {
43         for (int c = 0; c < chan; c++)
44         {
45             leftBorder [b*chan + c] = leftBorder [borderSize*chan + c];
46             rightBorder[b*chan + c] = rightBorder[-chan + c];
47         }
48     }
49 }
50
51 template<typename T>
52 void fillBorderReflectRow(uint8_t* row, int length, int chan, int borderSize)
53 {
54     auto leftBorder  = reinterpret_cast<T*>(row);
55     auto rightBorder = leftBorder + (length - borderSize) * chan;
56     for (int b = 0; b < borderSize; b++)
57     {
58         for (int c = 0; c < chan; c++)
59         {
60             leftBorder [b*chan + c] = leftBorder [(2*borderSize - b)*chan + c];
61             rightBorder[b*chan + c] = rightBorder[(-b - 2)*chan + c];
62         }
63     }
64 }
65
66 template<typename T>
67 void fillConstBorderRow(uint8_t* row, int length, int chan, int borderSize, cv::gapi::own::Scalar borderValue)
68 {
69     GAPI_DbgAssert(chan > 0 && chan <= 4);
70
71     auto leftBorder  = reinterpret_cast<T*>(row);
72     auto rightBorder = leftBorder + (length - borderSize) * chan;
73     for (int b = 0; b < borderSize; b++)
74     {
75         for (int c = 0; c < chan; c++)
76         {
77             leftBorder [b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);
78             rightBorder[b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);
79         }
80     }
81 }
82
83 // Fills const border pixels in the whole mat
84 void fillBorderConstant(int borderSize, cv::gapi::own::Scalar borderValue, cv::gapi::own::Mat& mat)
85 {
86     // cv::Scalar can contain maximum 4 chan
87     GAPI_Assert(mat.channels() > 0 && mat.channels() <= 4);
88
89     auto getFillBorderRowFunc = [&](int type) {
90         switch(type)
91         {
92         case CV_8U:  return &fillConstBorderRow< uint8_t>; break;
93         case CV_16S: return &fillConstBorderRow< int16_t>; break;
94         case CV_16U: return &fillConstBorderRow<uint16_t>; break;
95         case CV_32F: return &fillConstBorderRow< float  >; break;
96         default: GAPI_Assert(false); return &fillConstBorderRow<uint8_t>;
97         }
98     };
99
100     auto fillBorderRow = getFillBorderRowFunc(mat.depth());
101     for (int y = 0; y < mat.rows; y++)
102     {
103         fillBorderRow(mat.ptr(y), mat.cols, mat.channels(), borderSize, borderValue);
104     }
105 }
106 } // anonymous namespace
107
108 fluid::BorderHandler::BorderHandler(int border_size)
109 {
110     GAPI_Assert(border_size > 0);
111     m_border_size = border_size;
112 }
113
114 template <int BorderType>
115 fluid::BorderHandlerT<BorderType>::BorderHandlerT(int border_size, int data_type)
116     : BorderHandler(border_size)
117 {
118     auto getFillBorderRowFunc = [&](int border, int depth) {
119         if (border == cv::BORDER_REPLICATE)
120         {
121             switch(depth)
122             {
123             case CV_8U:  return &fillBorderReplicateRow< uint8_t>; break;
124             case CV_16S: return &fillBorderReplicateRow< int16_t>; break;
125             case CV_16U: return &fillBorderReplicateRow<uint16_t>; break;
126             case CV_32F: return &fillBorderReplicateRow< float  >; break;
127             default: GAPI_Assert(!"Unsupported data type"); return &fillBorderReplicateRow<uint8_t>;
128             }
129         }
130         else if (border == cv::BORDER_REFLECT_101)
131         {
132             switch(depth)
133             {
134             case CV_8U:  return &fillBorderReflectRow< uint8_t>; break;
135             case CV_16S: return &fillBorderReflectRow< int16_t>; break;
136             case CV_16U: return &fillBorderReflectRow<uint16_t>; break;
137             case CV_32F: return &fillBorderReflectRow< float  >; break;
138             default: GAPI_Assert(!"Unsupported data type"); return &fillBorderReflectRow<uint8_t>;
139             }
140         }
141         else
142         {
143             GAPI_Assert(!"Unsupported border type");
144             return &fillBorderReflectRow<uint8_t>;
145         }
146     };
147
148     m_fill_border_row = getFillBorderRowFunc(BorderType, CV_MAT_DEPTH(data_type));
149 }
150
151 namespace {
152 template <int BorderType> int getBorderIdx(int log_idx, int desc_height);
153
154 template<> int getBorderIdx<cv::BORDER_REPLICATE>(int log_idx, int desc_height)
155 {
156     return log_idx < 0 ? 0 : desc_height - 1;
157 }
158
159 template<> int getBorderIdx<cv::BORDER_REFLECT_101>(int log_idx, int desc_height)
160 {
161     return log_idx < 0 ? -log_idx : 2*(desc_height - 1) - log_idx;
162 }
163 } // namespace
164
165 template <int BorderType>
166 const uint8_t* fluid::BorderHandlerT<BorderType>::inLineB(int log_idx, const BufferStorageWithBorder& data, int desc_height) const
167 {
168     auto idx = getBorderIdx<BorderType>(log_idx, desc_height);
169     return data.ptr(idx);
170 }
171
172 fluid::BorderHandlerT<cv::BORDER_CONSTANT>::BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value)
173     : BorderHandler(border_size), m_border_value(border_value)
174 { /* nothing */ }
175
176 const uint8_t* fluid::BorderHandlerT<cv::BORDER_CONSTANT>::inLineB(int /*log_idx*/, const BufferStorageWithBorder& /*data*/, int /*desc_height*/) const
177 {
178     return m_const_border.ptr(0, m_border_size);
179 }
180
181 void fluid::BorderHandlerT<cv::BORDER_CONSTANT>::fillCompileTimeBorder(BufferStorageWithBorder& data)
182 {
183     m_const_border.create(1, data.cols(), data.data().type());
184     m_const_border = m_border_value;
185
186     cv::gapi::fillBorderConstant(m_border_size, m_border_value, data.data());
187 }
188
189 template <int BorderType>
190 void fluid::BorderHandlerT<BorderType>::updateBorderPixels(BufferStorageWithBorder &data, int startLine, int nLines) const
191 {
192     auto& mat   = data.data();
193     auto length = mat.cols;
194     auto chan   = mat.channels();
195
196     for (int l = startLine; l < startLine + nLines; l++)
197     {
198         auto row = mat.ptr(data.physIdx(l));
199         m_fill_border_row(row, length, chan, m_border_size);
200     }
201 }
202
203 std::size_t fluid::BorderHandlerT<cv::BORDER_CONSTANT>::size() const
204 {
205     return m_const_border.total() * m_const_border.elemSize();
206 }
207
208 // Fluid BufferStorage implementation //////////////////////////////////////////
209
210 void fluid::BufferStorage::updateInCache(View::Cache& cache, int start_log_idx, int nLines) const
211 {
212     for (int i = 0; i < nLines; i++)
213     {
214         cache.m_linePtrs[i] = inLineB(start_log_idx + i, cache.m_desc.size.height);
215     }
216 }
217
218 void fluid::BufferStorage::updateOutCache(Buffer::Cache& cache, int start_log_idx, int nLines)
219 {
220     for (int i = 0; i < nLines; i++)
221     {
222         cache.m_linePtrs[i] = ptr(start_log_idx + i);
223     }
224 }
225
226 void fluid::BufferStorageWithBorder::init(int dtype, int border_size, Border border)
227 {
228     switch(border.type)
229     {
230     case cv::BORDER_CONSTANT:
231         m_borderHandler.reset(new BorderHandlerT<cv::BORDER_CONSTANT>(border_size, border.value)); break;
232     case cv::BORDER_REPLICATE:
233         m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REPLICATE>(border_size, dtype)); break;
234     case cv::BORDER_REFLECT_101:
235         m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REFLECT_101>(border_size, dtype)); break;
236     default:
237         GAPI_Assert(false);
238     }
239 }
240
241 void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dtype)
242 {
243     auto borderSize = m_borderHandler->borderSize();
244     auto width = (desc_width + 2*borderSize);
245     m_data.create(capacity, width, dtype);
246
247     m_borderHandler->fillCompileTimeBorder(*this);
248 }
249
250 void fluid::BufferStorageWithoutBorder::create(int capacity, int desc_width, int dtype)
251 {
252     auto width = desc_width;
253     m_data.create(capacity, width, dtype);
254
255     m_is_virtual = true;
256 }
257
258 const uint8_t* fluid::BufferStorageWithBorder::inLineB(int log_idx, int desc_height) const
259 {
260     if (log_idx < 0 || log_idx >= desc_height)
261     {
262         return m_borderHandler->inLineB(log_idx, *this, desc_height);
263     }
264     else
265     {
266         return ptr(log_idx);
267     }
268 }
269
270 static void copyWithoutBorder(const cv::gapi::own::Mat& src, int src_border_size, cv::gapi::own::Mat& dst, int dst_border_size, int startSrcLine, int startDstLine, int lpi)
271 {
272     auto subSrc = src(cv::gapi::own::Rect{src_border_size, startSrcLine, src.cols - 2*src_border_size, lpi});
273     auto subDst = dst(cv::gapi::own::Rect{dst_border_size, startDstLine, dst.cols - 2*dst_border_size, lpi});
274
275     subSrc.copyTo(subDst);
276 }
277
278 void fluid::BufferStorageWithoutBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const
279 {
280     for (int l = startLine; l < startLine + nLines; l++)
281     {
282         copyWithoutBorder(m_data, 0, dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);
283     }
284 }
285
286 void fluid::BufferStorageWithBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const
287 {
288     // Copy required lpi lines line by line (to avoid wrap if invoked for multiple lines)
289     for (int l = startLine; l < startLine + nLines; l++)
290     {
291         copyWithoutBorder(m_data, borderSize(), dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);
292     }
293 }
294
295 // FIXME? remember parent and remove src parameter?
296 void fluid::BufferStorageWithBorder::updateBeforeRead(int startLine, int nLines, const BufferStorage& src)
297 {
298     // TODO:
299     // Cover with tests!!
300     // (Ensure that there are no redundant copies done
301     // and only required (not fetched before) lines are copied)
302
303     GAPI_DbgAssert(startLine >= 0);
304
305     src.copyTo(*this, startLine, nLines);
306     m_borderHandler->updateBorderPixels(*this, startLine, nLines);
307 }
308
309 void fluid::BufferStorageWithoutBorder::updateBeforeRead(int /*startLine*/, int /*lpi*/, const BufferStorage& /*src*/)
310 {
311     /* nothing */
312 }
313
314 void fluid::BufferStorageWithBorder::updateAfterWrite(int startLine, int nLines)
315 {
316     // FIXME?
317     // Actually startLine + nLines can be > logical height so
318     // redundant end lines which will never be read
319     // can be filled in the ring buffer
320     m_borderHandler->updateBorderPixels(*this, startLine, nLines);
321 }
322
323 void fluid::BufferStorageWithoutBorder::updateAfterWrite(int /*startLine*/, int /*lpi*/)
324 {
325     /* nothing */
326 }
327
328 size_t fluid::BufferStorageWithBorder::size() const
329 {
330     return m_data.total()*m_data.elemSize() + m_borderHandler->size();
331 }
332
333 size_t fluid::BufferStorageWithoutBorder::size() const
334 {
335     return m_data.total()*m_data.elemSize();
336 }
337
338 namespace fluid {
339 namespace {
340 std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,
341                                                     int border_size, fluid::BorderOpt border);
342 std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,
343                                                     int border_size, fluid::BorderOpt border)
344 {
345     if (border)
346     {
347         std::unique_ptr<fluid::BufferStorageWithBorder> storage(new BufferStorageWithBorder);
348         storage->init(type, border_size, border.value());
349         storage->create(capacity, desc_width, type);
350         return std::move(storage);
351     }
352
353     std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);
354     storage->create(capacity, desc_width, type);
355     return std::move(storage);
356 }
357
358 std::unique_ptr<BufferStorage> createStorage(const cv::gapi::own::Mat& data, cv::gapi::own::Rect roi);
359 std::unique_ptr<BufferStorage> createStorage(const cv::gapi::own::Mat& data, cv::gapi::own::Rect roi)
360 {
361     std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);
362     storage->attach(data, roi);
363     return std::move(storage);
364 }
365 } // namespace
366 } // namespace fluid
367
368 // Fluid View implementation ///////////////////////////////////////////////////
369
370 void fluid::View::Priv::reset(int linesForFirstIteration)
371 {
372     GAPI_DbgAssert(m_p);
373
374     m_lines_next_iter = linesForFirstIteration;
375     m_read_caret = m_p->priv().readStart();
376 }
377
378 void fluid::View::Priv::readDone(int linesRead, int linesForNextIteration)
379 {
380     GAPI_DbgAssert(m_p);
381     m_read_caret += linesRead;
382     m_lines_next_iter = linesForNextIteration;
383 }
384
385 bool fluid::View::Priv::ready() const
386 {
387     auto lastWrittenLine = m_p->priv().writeStart() + m_p->linesReady();
388     // + bottom border
389     if (lastWrittenLine == m_p->meta().size.height) lastWrittenLine += m_border_size;
390     // + top border
391     lastWrittenLine += m_border_size;
392
393     auto lastRequiredLine = m_read_caret + m_lines_next_iter;
394
395     return lastWrittenLine >= lastRequiredLine;
396 }
397
398 fluid::ViewPrivWithoutOwnBorder::ViewPrivWithoutOwnBorder(const Buffer *parent, int borderSize)
399 {
400     GAPI_Assert(parent);
401     m_p           = parent;
402     m_border_size = borderSize;
403 }
404
405 const uint8_t* fluid::ViewPrivWithoutOwnBorder::InLineB(int index) const
406 {
407     GAPI_DbgAssert(m_p);
408
409     const auto &p_priv = m_p->priv();
410
411     GAPI_DbgAssert(index >= -m_border_size
412                 && index <  -m_border_size + m_lines_next_iter);
413
414     const int log_idx = m_read_caret + index;
415
416     return p_priv.storage().inLineB(log_idx, m_p->meta().size.height);
417 }
418
419 void fluid::ViewPrivWithoutOwnBorder::allocate(int lineConsumption, BorderOpt)
420 {
421     initCache(lineConsumption);
422 }
423
424 void fluid::ViewPrivWithoutOwnBorder::prepareToRead()
425 {
426     const auto &storage = m_p->priv().storage();
427
428     const int start_log_idx = m_read_caret - m_border_size;
429     storage.updateInCache(m_cache, start_log_idx, m_lines_next_iter);
430 }
431
432 fluid::ViewPrivWithOwnBorder::ViewPrivWithOwnBorder(const Buffer *parent, int borderSize)
433 {
434     GAPI_Assert(parent);
435     m_p           = parent;
436     m_border_size = borderSize;
437 }
438
439 void fluid::ViewPrivWithOwnBorder::allocate(int lineConsumption, BorderOpt border)
440 {
441     initCache(lineConsumption);
442
443     const auto& desc = m_cache.m_desc;
444     int  type = CV_MAKETYPE(desc.depth, desc.chan);
445     m_own_storage.init(type, m_border_size, border.value());
446     m_own_storage.create(lineConsumption, desc.size.width, type);
447 }
448
449 void fluid::ViewPrivWithOwnBorder::prepareToRead()
450 {
451     int startLine = 0;
452     int nLines = 0;
453
454     if (m_read_caret == m_p->priv().readStart())
455     {
456         // Need to fetch full window on the first iteration
457         startLine = (m_read_caret > m_border_size) ? m_read_caret - m_border_size : 0;
458         nLines = m_lines_next_iter;
459     }
460     else
461     {
462         startLine = m_read_caret + m_border_size;
463         nLines = m_lines_next_iter - 2*m_border_size;
464     }
465
466     m_own_storage.updateBeforeRead(startLine, nLines, m_p->priv().storage());
467
468     const int start_log_idx = m_read_caret - m_border_size;
469     m_own_storage.updateInCache(m_cache, start_log_idx, m_lines_next_iter);
470 }
471
472 std::size_t fluid::ViewPrivWithOwnBorder::size() const
473 {
474     GAPI_DbgAssert(m_p);
475     return m_own_storage.size();
476 }
477
478 const uint8_t* fluid::ViewPrivWithOwnBorder::InLineB(int index) const
479 {
480     GAPI_DbgAssert(m_p);
481     GAPI_DbgAssert(index >= -m_border_size
482                 && index <  -m_border_size + m_lines_next_iter);
483
484     const int log_idx = m_read_caret + index;
485
486     return m_own_storage.inLineB(log_idx, m_p->meta().size.height);
487 }
488
489 bool fluid::View::ready() const
490 {
491     return m_priv->ready();
492 }
493
494 int fluid::View::y() const
495 {
496     return m_priv->m_read_caret - m_priv->m_border_size;
497 }
498
499 fluid::View::Priv& fluid::View::priv()
500 {
501     return *m_priv;
502 }
503
504 const fluid::View::Priv& fluid::View::priv() const
505 {
506     return *m_priv;
507 }
508
509 void fluid::View::Priv::initCache(int lineConsumption)
510 {
511     m_cache.m_linePtrs.resize(lineConsumption);
512     m_cache.m_desc = m_p->priv().meta();
513     m_cache.m_border_size = m_border_size;
514 }
515
516 // Fluid Buffer implementation /////////////////////////////////////////////////
517
518 fluid::Buffer::Priv::Priv(int read_start, cv::gapi::own::Rect roi)
519     : m_readStart(read_start)
520     , m_roi(roi)
521 {}
522
523 void fluid::Buffer::Priv::init(const cv::GMatDesc &desc,
524                                int writer_lpi,
525                                int readStartPos,
526                                cv::gapi::own::Rect roi)
527 {
528     m_writer_lpi = writer_lpi;
529     m_desc       = desc;
530     m_readStart  = readStartPos;
531     m_roi        = roi == own::Rect{} ? own::Rect{ 0, 0, desc.size.width, desc.size.height }
532                                       : roi;
533     m_cache.m_linePtrs.resize(writer_lpi);
534     m_cache.m_desc = desc;
535 }
536
537 void fluid::Buffer::Priv::allocate(BorderOpt border,
538                                    int border_size,
539                                    int line_consumption,
540                                    int skew)
541 {
542     GAPI_Assert(line_consumption > 0);
543
544     // Init physical buffer
545
546     // FIXME? combine line_consumption with skew?
547     auto data_height = std::max(line_consumption, skew) + m_writer_lpi - 1;
548
549     m_storage = createStorage(data_height,
550                               m_desc.size.width,
551                               CV_MAKETYPE(m_desc.depth, m_desc.chan),
552                               border_size,
553                               border);
554
555     // Finally, initialize carets
556     m_write_caret = writeStart();
557
558     m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi);
559 }
560
561 void fluid::Buffer::Priv::bindTo(const cv::gapi::own::Mat &data, bool is_input)
562 {
563     // FIXME: move all these fields into a separate structure
564     GAPI_Assert(m_desc == descr_of(data));
565
566     // Currently m_writer_lpi is obtained from metadata which is shared between islands
567     // and this assert can trigger for slot which connects two fluid islands.
568     // m_writer_lpi is used only in write-related functions and doesn't affect
569     // buffer which is island's input so it's safe to skip this check.
570     // FIXME:
571     // Bring back this check when we move to 1 buffer <-> 1 metadata model
572     // if (is_input) GAPI_Assert(m_writer_lpi == 1);
573
574     m_storage = createStorage(data, m_roi);
575
576     m_is_input    = is_input;
577     m_write_caret = is_input ? writeEnd(): writeStart();
578     // NB: views remain the same!
579
580     m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi);
581 }
582
583 bool fluid::Buffer::Priv::full() const
584 {
585     int slowest_y = writeEnd();
586     if (!m_views.empty())
587     {
588         // reset with maximum possible value and then find minimum
589         slowest_y = m_desc.size.height;
590         for (const auto &v : m_views) slowest_y = std::min(slowest_y, v.y());
591     }
592
593     return m_write_caret + lpi() - slowest_y > m_storage->rows();
594 }
595
596 void fluid::Buffer::Priv::writeDone()
597 {
598     // There are possible optimizations which can be done to fill a border values
599     // in compile time of the graph (for example border is const),
600     // so there is no need to update border values after each write.
601     // If such optimizations weren't applied, fill border for lines
602     // which have been just written
603     m_storage->updateAfterWrite(m_write_caret, m_writer_lpi);
604
605     // Final write may produce less LPI, so
606     // write caret may exceed logical buffer size
607     m_write_caret += m_writer_lpi;
608     // FIXME: add consistency check!
609
610     m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi);
611 }
612
613 void fluid::Buffer::Priv::reset()
614 {
615     m_write_caret = m_is_input ? writeEnd() : writeStart();
616     m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi);
617 }
618
619 int fluid::Buffer::Priv::size() const
620 {
621     std::size_t view_sz = 0;
622     for (const auto &v : m_views) view_sz += v.priv().size();
623
624     auto total = view_sz;
625     if (m_storage) total += m_storage->size();
626
627     // FIXME: Change API to return size_t!!!
628     return static_cast<int>(total);
629 }
630
631 int fluid::Buffer::Priv::linesReady() const
632 {
633     if (m_is_input)
634     {
635         return m_storage->rows();
636     }
637     else
638     {
639         const int writes = std::min(m_write_caret - writeStart(), outputLines());
640         return writes;
641     }
642 }
643
644 uint8_t* fluid::Buffer::Priv::OutLineB(int index)
645 {
646     GAPI_DbgAssert(index >= 0 && index < m_writer_lpi);
647
648     return m_storage->ptr(m_write_caret + index);
649 }
650
651 int fluid::Buffer::Priv::lpi() const
652 {
653     // FIXME:
654     // m_write_caret can be greater than m_writeRoi.y + m_writeRoi.height, so return value can be negative !!!
655     return std::min(writeEnd() - m_write_caret, m_writer_lpi);
656 }
657
658 fluid::Buffer::Buffer()
659     : m_priv(new Priv())
660     , m_cache(&m_priv->cache())
661 {
662 }
663
664 fluid::Buffer::Buffer(const cv::GMatDesc &desc)
665     : m_priv(new Priv())
666     , m_cache(&m_priv->cache())
667 {
668     int lineConsumption = 1;
669     int border = 0, skew = 0, wlpi = 1, readStart = 0;
670     cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
671     m_priv->init(desc, wlpi, readStart, roi);
672     m_priv->allocate({}, border, lineConsumption, skew);
673 }
674
675 fluid::Buffer::Buffer(const cv::GMatDesc &desc,
676                       int max_line_consumption,
677                       int border_size,
678                       int skew,
679                       int wlpi,
680                       BorderOpt border)
681     : m_priv(new Priv())
682     , m_cache(&m_priv->cache())
683 {
684     int readStart = 0;
685     cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
686     m_priv->init(desc, wlpi, readStart, roi);
687     m_priv->allocate(border, border_size, max_line_consumption, skew);
688 }
689
690 fluid::Buffer::Buffer(const cv::gapi::own::Mat &data, bool is_input)
691     : m_priv(new Priv())
692     , m_cache(&m_priv->cache())
693 {
694     int wlpi = 1, readStart = 0;
695     cv::gapi::own::Rect roi{0, 0, data.cols, data.rows};
696     m_priv->init(descr_of(data), wlpi, readStart, roi);
697     m_priv->bindTo(data, is_input);
698 }
699
700 int fluid::Buffer::linesReady() const
701 {
702     return m_priv->linesReady();
703 }
704
705 int fluid::Buffer::lpi() const
706 {
707     return m_priv->lpi();
708 }
709
710 fluid::View::View(Priv* p)
711     : m_priv(p), m_cache(&p->cache())
712 { /* nothing */ }
713
714 fluid::View fluid::Buffer::mkView(int borderSize, bool ownStorage)
715 {
716     // FIXME: logic outside of Priv (because View takes pointer to Buffer)
717     auto view = ownStorage ? View(new ViewPrivWithOwnBorder(this, borderSize))
718                            : View(new ViewPrivWithoutOwnBorder(this, borderSize));
719     m_priv->addView(view);
720     return view;
721 }
722
723 void fluid::debugBufferPriv(const fluid::Buffer& buffer, std::ostream &os)
724 {
725     // FIXME Use cv::gapi::own Size and Rect with operator<<, when merged ADE-285
726     const auto& p = buffer.priv();
727     os << "Fluid buffer " << std::hex << &buffer << std::dec
728        << " " << p.m_desc.size.width << " x " << p.m_desc.size.height << "]"
729        << " readStart:" << p.m_readStart
730        << " roi:" << "[" << p.m_roi.width << " x " << p.m_roi.height << " from (" << p.m_roi.x << ", " << p.m_roi.y << ")]"
731        <<" (phys " << "[" << p.storage().cols() << " x " <<  p.storage().rows() << "]" << ") :"
732        << "  w: " << p.m_write_caret
733        << ", r: [";
734     for (const auto &v : p.m_views) { os << &v.priv() << ":" << v.y() << " "; }
735     os << "], avail: " << buffer.linesReady()
736        << std::endl;
737 }
738
739 void fluid::Buffer::debug(std::ostream &os) const
740 {
741     debugBufferPriv(*this, os);
742 }
743
744 fluid::Buffer::Priv& fluid::Buffer::priv()
745 {
746     return *m_priv;
747 }
748
749 const fluid::Buffer::Priv& fluid::Buffer::priv() const
750 {
751     return *m_priv;
752 }
753
754 int fluid::Buffer::y() const
755 {
756     return m_priv->y();
757 }
758
759 } // namespace cv::gapi
760 } // namespace cv