1 /*
2 Copyright (c) 2005-2021 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 // Polygon overlay
18 //
19 #include <cstdlib>
20 #include <cstring>
21 #include <cassert>
22
23 #include <iostream>
24 #include <algorithm>
25
26 #include "oneapi/tbb/tick_count.h"
27 #include "oneapi/tbb/blocked_range.h"
28 #include "oneapi/tbb/parallel_for.h"
29 #include "oneapi/tbb/spin_mutex.h"
30 #include "oneapi/tbb/global_control.h"
31
32 #include "common/utility/get_default_num_threads.hpp"
33
34 #include "polyover.hpp"
35 #include "polymain.hpp"
36 #include "pover_video.hpp"
37
38 /*!
39 * @brief intersects a polygon with a map, adding any results to output map
40 *
41 * @param[out] resultMap output map (must be allocated)
42 * @param[in] polygon to be intersected
43 * @param[in] map intersected against
44 * @param[in] lock to use when adding output polygons to result map
45 *
46 */
OverlayOnePolygonWithMap(Polygon_map_t * resultMap,RPolygon * myPoly,Polygon_map_t * map2,oneapi::tbb::spin_mutex * rMutex)47 void OverlayOnePolygonWithMap(Polygon_map_t *resultMap,
48 RPolygon *myPoly,
49 Polygon_map_t *map2,
50 oneapi::tbb::spin_mutex *rMutex) {
51 int r1, g1, b1, r2, g2, b2;
52 int myr = 0;
53 int myg = 0;
54 int myb = 0;
55 int p1Area = myPoly->area();
56 for (unsigned int j = 1; (j < map2->size()) && (p1Area > 0); j++) {
57 RPolygon *p2 = &((*map2)[j]);
58 RPolygon *pnew;
59 int newxMin, newxMax, newyMin, newyMax;
60 myPoly->getColor(&r1, &g1, &b1);
61 if (PolygonsOverlap(myPoly, p2, newxMin, newyMin, newxMax, newyMax)) {
62 p2->getColor(&r2, &g2, &b2);
63 myr = r1 + r2;
64 myg = g1 + g2;
65 myb = b1 + b2;
66 p1Area -= (newxMax - newxMin + 1) * (newyMax - newyMin + 1);
67 if (rMutex) {
68 oneapi::tbb::spin_mutex::scoped_lock lock(*rMutex);
69 resultMap->push_back(RPolygon(newxMin, newyMin, newxMax, newyMax, myr, myg, myb));
70 }
71 else {
72 resultMap->push_back(RPolygon(newxMin, newyMin, newxMax, newyMax, myr, myg, myb));
73 }
74 }
75 }
76 }
77
78 /*!
79 * @brief Serial version of polygon overlay
80 * @param[out] output map
81 * @param[in] first map (map that individual polygons are taken from)
82 * @param[in] second map (map passed to OverlayOnePolygonWithMap)
83 */
SerialOverlayMaps(Polygon_map_t ** resultMap,Polygon_map_t * map1,Polygon_map_t * map2)84 void SerialOverlayMaps(Polygon_map_t **resultMap, Polygon_map_t *map1, Polygon_map_t *map2) {
85 std::cout << "SerialOverlayMaps called"
86 << "\n";
87 *resultMap = new Polygon_map_t;
88
89 RPolygon *p0 = &((*map1)[0]);
90 int mapxSize, mapySize, ignore1, ignore2;
91 p0->get(&ignore1, &ignore2, &mapxSize, &mapySize);
92 (*resultMap)->reserve(mapxSize * mapySize); // can't be any bigger than this
93 // push the map size as the first polygon,
94 (*resultMap)->push_back(RPolygon(0, 0, mapxSize, mapySize));
95 for (unsigned int i = 1; i < map1->size(); i++) {
96 RPolygon *p1 = &((*map1)[i]);
97 OverlayOnePolygonWithMap(*resultMap, p1, map2, nullptr);
98 }
99 }
100
101 /*!
102 * @class ApplyOverlay
103 * @brief Simple version of parallel overlay (make parallel on polygons in map1)
104 */
105 class ApplyOverlay {
106 Polygon_map_t *m_map1, *m_map2, *m_resultMap;
107 oneapi::tbb::spin_mutex *m_rMutex;
108
109 public:
110 /*!
111 * @brief functor to apply
112 * @param[in] r range of polygons to intersect from map1
113 */
operator ()(const oneapi::tbb::blocked_range<int> & r) const114 void operator()(const oneapi::tbb::blocked_range<int> &r) const {
115 PRINT_DEBUG("From " << r.begin() << " to " << r.end());
116 for (int i = r.begin(); i != r.end(); i++) {
117 RPolygon *myPoly = &((*m_map1)[i]);
118 OverlayOnePolygonWithMap(m_resultMap, myPoly, m_map2, m_rMutex);
119 }
120 }
ApplyOverlay(Polygon_map_t * resultMap,Polygon_map_t * map1,Polygon_map_t * map2,oneapi::tbb::spin_mutex * rmutex)121 ApplyOverlay(Polygon_map_t *resultMap,
122 Polygon_map_t *map1,
123 Polygon_map_t *map2,
124 oneapi::tbb::spin_mutex *rmutex)
125 : m_resultMap(resultMap),
126 m_map1(map1),
127 m_map2(map2),
128 m_rMutex(rmutex) {}
129 };
130
131 /*!
132 * @brief apply the parallel algorithm
133 * @param[out] result_map generated map
134 * @param[in] polymap1 first map to be applied (algorithm is parallel on this map)
135 * @param[in] polymap2 second map.
136 */
NaiveParallelOverlay(Polygon_map_t * & result_map,Polygon_map_t & polymap1,Polygon_map_t & polymap2)137 void NaiveParallelOverlay(Polygon_map_t *&result_map,
138 Polygon_map_t &polymap1,
139 Polygon_map_t &polymap2) {
140 // -----------------------------------
141 bool automatic_threadcount = false;
142
143 if (gThreadsLow == THREADS_UNSET || gThreadsLow == utility::get_default_num_threads()) {
144 gThreadsLow = gThreadsHigh = utility::get_default_num_threads();
145 automatic_threadcount = true;
146 }
147 result_map = new Polygon_map_t;
148
149 RPolygon *p0 = &(polymap1[0]);
150 int mapxSize, mapySize, ignore1, ignore2;
151 p0->get(&ignore1, &ignore2, &mapxSize, &mapySize);
152 result_map->reserve(mapxSize * mapySize); // can't be any bigger than this
153 // push the map size as the first polygon,
154 oneapi::tbb::spin_mutex *resultMutex = new oneapi::tbb::spin_mutex();
155 int grain_size = gGrainSize;
156
157 for (int nthreads = gThreadsLow; nthreads <= gThreadsHigh; nthreads++) {
158 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
159 nthreads);
160 if (gIsGraphicalVersion) {
161 RPolygon *xp =
162 new RPolygon(0, 0, gMapXSize - 1, gMapYSize - 1, 0, 0, 0); // Clear the output space
163 delete xp;
164 }
165 // put size polygon in result map
166 result_map->push_back(RPolygon(0, 0, mapxSize, mapySize));
167
168 oneapi::tbb::tick_count t0 = oneapi::tbb::tick_count::now();
169 oneapi::tbb::parallel_for(
170 oneapi::tbb::blocked_range<int>(1, (int)(polymap1.size()), grain_size),
171 ApplyOverlay(result_map, &polymap1, &polymap2, resultMutex));
172 oneapi::tbb::tick_count t1 = oneapi::tbb::tick_count::now();
173
174 double naiveParallelTime = (t1 - t0).seconds() * 1000;
175 std::cout << "Naive parallel with spin lock and ";
176 if (automatic_threadcount)
177 std::cout << "automatic";
178 else
179 std::cout << nthreads;
180 std::cout << ((nthreads == 1) ? " thread" : " threads");
181 std::cout << " took " << naiveParallelTime << " msec : speedup over serial "
182 << (gSerialTime / naiveParallelTime) << "\n";
183 if (gCsvFile.is_open()) {
184 gCsvFile << "," << naiveParallelTime;
185 }
186 #if _DEBUG
187 CheckPolygonMap(result_map);
188 ComparePolygonMaps(result_map, gResultMap);
189 #endif
190 result_map->clear();
191 }
192 delete resultMutex;
193 if (gCsvFile.is_open()) {
194 gCsvFile << "\n";
195 }
196 // -----------------------------------
197 }
198
199 template <typename T>
split_at(Flagged_map_t & in_map,Flagged_map_t & left_out,Flagged_map_t & right_out,const T median)200 void split_at(Flagged_map_t &in_map,
201 Flagged_map_t &left_out,
202 Flagged_map_t &right_out,
203 const T median) {
204 left_out.reserve(in_map.size());
205 right_out.reserve(in_map.size());
206 for (Flagged_map_t::iterator i = in_map.begin(); i != in_map.end(); ++i) {
207 RPolygon *p = i->p();
208 if (p->xmax() < median) {
209 // in left map
210 left_out.push_back(*i);
211 }
212 else if (p->xmin() >= median) {
213 right_out.push_back(*i);
214 // in right map
215 }
216 else {
217 // in both maps.
218 left_out.push_back(*i);
219 right_out.push_back(RPolygon_flagged(p, true));
220 }
221 }
222 }
223
224 // range that splits the maps as well as the range. the flagged_map_t are
225 // vectors of pointers, and each range owns its maps (has to free them on destruction.)
226 template <typename T>
227 class blocked_range_with_maps {
228 typedef oneapi::tbb::blocked_range<T> my_range_type;
229
230 private:
231 my_range_type my_range;
232 Flagged_map_t my_map1;
233 Flagged_map_t my_map2;
234
235 public:
blocked_range_with_maps(T begin,T end,typename my_range_type::size_type my_grainsize,Polygon_map_t * p1,Polygon_map_t * p2)236 blocked_range_with_maps(T begin,
237 T end,
238 typename my_range_type::size_type my_grainsize,
239 Polygon_map_t *p1,
240 Polygon_map_t *p2)
241 : my_range(begin, end, my_grainsize) {
242 my_map1.reserve(p1->size());
243 my_map2.reserve(p2->size());
244 for (int i = 1; i < p1->size(); ++i) {
245 my_map1.push_back(RPolygon_flagged(&((*p1)[i]), false));
246 }
247 for (int i = 1; i < p2->size(); ++i) {
248 my_map2.push_back(RPolygon_flagged(&(p2->at(i)), false));
249 }
250 }
251
252 // copy-constructor required for deep copy of flagged maps. One copy is done at the start of the
253 // parallel for.
blocked_range_with_maps(const blocked_range_with_maps & other)254 blocked_range_with_maps(const blocked_range_with_maps &other)
255 : my_range(other.my_range),
256 my_map1(other.my_map1),
257 my_map2(other.my_map2) {}
empty() const258 bool empty() const {
259 return my_range.empty();
260 }
is_divisible() const261 bool is_divisible() const {
262 return my_range.is_divisible();
263 }
264
265 #if _DEBUG
check_my_map()266 void check_my_map() {
267 assert(my_range.begin() <= my_range.end());
268 for (Flagged_map_t::iterator i = my_map1.begin(); i != my_map1.end(); ++i) {
269 RPolygon *rp = i->p();
270 assert(rp->xmax() >= my_range.begin());
271 assert(rp->xmin() < my_range.end());
272 }
273 for (Flagged_map_t::iterator i = my_map2.begin(); i != my_map2.end(); ++i) {
274 RPolygon *rp = i->p();
275 assert(rp->xmax() >= my_range.begin());
276 assert(rp->xmin() < my_range.end());
277 }
278 }
279
dump_map(Flagged_map_t & mapx)280 void dump_map(Flagged_map_t &mapx) {
281 std::cout << " ** MAP **\n";
282 for (Flagged_map_t::iterator i = mapx.begin(); i != mapx.end(); ++i) {
283 std::cout << *(i->p());
284 if (i->isDuplicate()) {
285 std::cout << " -- is_duplicate";
286 }
287 std::cout << "\n";
288 }
289 std::cout << "\n";
290 }
291 #endif
292
blocked_range_with_maps(blocked_range_with_maps & lhs_r,oneapi::tbb::split)293 blocked_range_with_maps(blocked_range_with_maps &lhs_r, oneapi::tbb::split)
294 : my_range(my_range_type(lhs_r.my_range, oneapi::tbb::split())) {
295 // lhs_r.my_range makes my_range from [median, high) and rhs_r.my_range from [low, median)
296 Flagged_map_t original_map1 = lhs_r.my_map1;
297 Flagged_map_t original_map2 = lhs_r.my_map2;
298 lhs_r.my_map1.clear();
299 lhs_r.my_map2.clear();
300 split_at(original_map1, lhs_r.my_map1, my_map1, my_range.begin());
301 split_at(original_map2, lhs_r.my_map2, my_map2, my_range.begin());
302 #if _DEBUG
303 this->check_my_map();
304 lhs_r.check_my_map();
305 #endif
306 }
307
range() const308 const my_range_type &range() const {
309 return my_range;
310 }
map1()311 Flagged_map_t &map1() {
312 return my_map1;
313 }
map2()314 Flagged_map_t &map2() {
315 return my_map2;
316 }
317 };
318
319 /*!
320 * @class ApplySplitOverlay
321 * @brief parallel by columnar strip
322 */
323 class ApplySplitOverlay {
324 Polygon_map_t *m_map1, *m_map2, *m_resultMap;
325 oneapi::tbb::spin_mutex *m_rMutex;
326
327 public:
328 /*!
329 * @brief functor for columnar parallel version
330 * @param[in] r range of map to be operated on
331 */
operator ()(blocked_range_with_maps<int> & r) const332 void operator()(/*const*/ blocked_range_with_maps<int> &r) const {
333 #ifdef _DEBUG
334 // if we are debugging, serialize the method. That way we can
335 // see what is happening in each strip without the interleaving
336 // confusing things.
337 oneapi::tbb::spin_mutex::scoped_lock lock(*m_rMutex);
338 std::cout << std::unitbuf << "From " << r.range().begin() << " to " << r.range().end() - 1
339 << "\n";
340 #endif
341 // get yMapSize
342 int r1, g1, b1, r2, g2, b2;
343 int myr = -1;
344 int myg = -1;
345 int myb = -1;
346 int i1, i2, i3, yMapSize;
347 (*m_map1)[0].get(&i1, &i2, &i3, &yMapSize);
348
349 Flagged_map_t &fmap1 = r.map1();
350 Flagged_map_t &fmap2 = r.map2();
351
352 // When intersecting polygons from fmap1 and fmap2, if BOTH are flagged
353 // as duplicate, don't add the result to the output map. We can still
354 // intersect them, because we are keeping track of how much of the polygon
355 // is left over from intersecting, and quitting when the polygon is
356 // used up.
357
358 for (unsigned int i = 0; i < fmap1.size(); i++) {
359 RPolygon *p1 = fmap1[i].p();
360 bool is_dup = fmap1[i].isDuplicate();
361 int parea = p1->area();
362 p1->getColor(&r1, &g1, &b1);
363 for (unsigned int j = 0; (j < fmap2.size()) && (parea > 0); j++) {
364 int xl, yl, xh, yh;
365 RPolygon *p2 = fmap2[j].p();
366 if (PolygonsOverlap(p1, p2, xl, yl, xh, yh)) {
367 if (!(is_dup && fmap2[j].isDuplicate())) {
368 p2->getColor(&r2, &g2, &b2);
369 myr = r1 + r2;
370 myg = g1 + g2;
371 myb = b1 + b2;
372 #ifdef _DEBUG
373 #else
374 oneapi::tbb::spin_mutex::scoped_lock lock(*m_rMutex);
375 #endif
376 (*m_resultMap).push_back(RPolygon(xl, yl, xh, yh, myr, myg, myb));
377 }
378 parea -= (xh - xl + 1) * (yh - yl + 1);
379 }
380 }
381 }
382 }
383
ApplySplitOverlay(Polygon_map_t * resultMap,Polygon_map_t * map1,Polygon_map_t * map2,oneapi::tbb::spin_mutex * rmutex)384 ApplySplitOverlay(Polygon_map_t *resultMap,
385 Polygon_map_t *map1,
386 Polygon_map_t *map2,
387 oneapi::tbb::spin_mutex *rmutex)
388 : m_resultMap(resultMap),
389 m_map1(map1),
390 m_map2(map2),
391 m_rMutex(rmutex) {}
392 };
393
394 /*!
395 * @brief intersects two maps strip-wise
396 *
397 * @param[out] resultMap output map (must be allocated)
398 * @param[in] polymap1 map to be intersected
399 * @param[in] polymap2 map to be intersected
400 */
SplitParallelOverlay(Polygon_map_t ** result_map,Polygon_map_t * polymap1,Polygon_map_t * polymap2)401 void SplitParallelOverlay(Polygon_map_t **result_map,
402 Polygon_map_t *polymap1,
403 Polygon_map_t *polymap2) {
404 int nthreads;
405 bool automatic_threadcount = false;
406 double domainSplitParallelTime;
407 oneapi::tbb::tick_count t0, t1;
408 oneapi::tbb::spin_mutex *resultMutex;
409 if (gThreadsLow == THREADS_UNSET || gThreadsLow == utility::get_default_num_threads()) {
410 gThreadsLow = gThreadsHigh = utility::get_default_num_threads();
411 automatic_threadcount = true;
412 }
413 *result_map = new Polygon_map_t;
414
415 RPolygon *p0 = &((*polymap1)[0]);
416 int mapxSize, mapySize, ignore1, ignore2;
417 p0->get(&ignore1, &ignore2, &mapxSize, &mapySize);
418 (*result_map)->reserve(mapxSize * mapySize); // can't be any bigger than this
419 resultMutex = new oneapi::tbb::spin_mutex();
420
421 int grain_size;
422 #ifdef _DEBUG
423 grain_size = gMapXSize / 4;
424 #else
425 grain_size = gGrainSize;
426 #endif
427 for (nthreads = gThreadsLow; nthreads <= gThreadsHigh; nthreads++) {
428 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
429 nthreads);
430 if (gIsGraphicalVersion) {
431 RPolygon *xp =
432 new RPolygon(0, 0, gMapXSize - 1, gMapYSize - 1, 0, 0, 0); // Clear the output space
433 delete xp;
434 }
435 // push the map size as the first polygon,
436 (*result_map)->push_back(RPolygon(0, 0, mapxSize, mapySize));
437 t0 = oneapi::tbb::tick_count::now();
438 oneapi::tbb::parallel_for(
439 blocked_range_with_maps<int>(0, (int)(mapxSize + 1), grain_size, polymap1, polymap2),
440 ApplySplitOverlay((*result_map), polymap1, polymap2, resultMutex));
441 t1 = oneapi::tbb::tick_count::now();
442 domainSplitParallelTime = (t1 - t0).seconds() * 1000;
443 std::cout << "Splitting parallel with spin lock and ";
444 if (automatic_threadcount)
445 std::cout << "automatic";
446 else
447 std::cout << nthreads;
448 std::cout << ((nthreads == 1) ? " thread" : " threads");
449 std::cout << " took " << domainSplitParallelTime << " msec : speedup over serial "
450 << (gSerialTime / domainSplitParallelTime) << "\n";
451 if (gCsvFile.is_open()) {
452 gCsvFile << "," << domainSplitParallelTime;
453 }
454 #if _DEBUG
455 CheckPolygonMap(*result_map);
456 ComparePolygonMaps(*result_map, gResultMap);
457 #endif
458 (*result_map)->clear();
459 }
460 delete resultMutex;
461 if (gCsvFile.is_open()) {
462 gCsvFile << "\n";
463 }
464 }
465
466 class ApplySplitOverlayCV {
467 Polygon_map_t *m_map1, *m_map2;
468 concurrent_Polygon_map_t *m_resultMap;
469
470 public:
471 /*!
472 * @brief functor for columnar parallel version
473 * @param[in] r range of map to be operated on
474 */
operator ()(blocked_range_with_maps<int> & r) const475 void operator()(blocked_range_with_maps<int> &r) const {
476 // get yMapSize
477 int r1, g1, b1, r2, g2, b2;
478 int myr = -1;
479 int myg = -1;
480 int myb = -1;
481 int i1, i2, i3, yMapSize;
482 (*m_map1)[0].get(&i1, &i2, &i3, &yMapSize);
483
484 Flagged_map_t &fmap1 = r.map1();
485 Flagged_map_t &fmap2 = r.map2();
486
487 // When intersecting polygons from fmap1 and fmap2, if BOTH are flagged
488 // as duplicate, don't add the result to the output map. We can still
489 // intersect them, because we are keeping track of how much of the polygon
490 // is left over from intersecting, and quitting when the polygon is
491 // used up.
492
493 for (unsigned int i = 0; i < fmap1.size(); i++) {
494 RPolygon *p1 = fmap1[i].p();
495 bool is_dup = fmap1[i].isDuplicate();
496 int parea = p1->area();
497 p1->getColor(&r1, &g1, &b1);
498 for (unsigned int j = 0; (j < fmap2.size()) && (parea > 0); j++) {
499 int xl, yl, xh, yh;
500 RPolygon *p2 = fmap2[j].p();
501 if (PolygonsOverlap(p1, p2, xl, yl, xh, yh)) {
502 if (!(is_dup && fmap2[j].isDuplicate())) {
503 p2->getColor(&r2, &g2, &b2);
504 myr = r1 + r2;
505 myg = g1 + g2;
506 myb = b1 + b2;
507 (*m_resultMap).push_back(RPolygon(xl, yl, xh, yh, myr, myg, myb));
508 }
509 parea -= (xh - xl + 1) * (yh - yl + 1);
510 }
511 }
512 }
513 }
514
ApplySplitOverlayCV(concurrent_Polygon_map_t * resultMap,Polygon_map_t * map1,Polygon_map_t * map2)515 ApplySplitOverlayCV(concurrent_Polygon_map_t *resultMap,
516 Polygon_map_t *map1,
517 Polygon_map_t *map2)
518 : m_resultMap(resultMap),
519 m_map1(map1),
520 m_map2(map2) {}
521 };
522
523 /*!
524 * @brief intersects two maps strip-wise, accumulating into a concurrent_vector
525 *
526 * @param[out] resultMap output map (must be allocated)
527 * @param[in] polymap1 map to be intersected
528 * @param[in] polymap2 map to be intersected
529 */
SplitParallelOverlayCV(concurrent_Polygon_map_t ** result_map,Polygon_map_t * polymap1,Polygon_map_t * polymap2)530 void SplitParallelOverlayCV(concurrent_Polygon_map_t **result_map,
531 Polygon_map_t *polymap1,
532 Polygon_map_t *polymap2) {
533 int nthreads;
534 bool automatic_threadcount = false;
535 double domainSplitParallelTime;
536 oneapi::tbb::tick_count t0, t1;
537 if (gThreadsLow == THREADS_UNSET || gThreadsLow == utility::get_default_num_threads()) {
538 gThreadsLow = gThreadsHigh = utility::get_default_num_threads();
539 automatic_threadcount = true;
540 }
541 *result_map = new concurrent_Polygon_map_t;
542
543 RPolygon *p0 = &((*polymap1)[0]);
544 int mapxSize, mapySize, ignore1, ignore2;
545 p0->get(&ignore1, &ignore2, &mapxSize, &mapySize);
546 // (*result_map)->reserve(mapxSize*mapySize); // can't be any bigger than this
547
548 int grain_size;
549 #ifdef _DEBUG
550 grain_size = gMapXSize / 4;
551 #else
552 grain_size = gGrainSize;
553 #endif
554 for (nthreads = gThreadsLow; nthreads <= gThreadsHigh; nthreads++) {
555 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
556 nthreads);
557 if (gIsGraphicalVersion) {
558 RPolygon *xp =
559 new RPolygon(0, 0, gMapXSize - 1, gMapYSize - 1, 0, 0, 0); // Clear the output space
560 delete xp;
561 }
562 // push the map size as the first polygon,
563 (*result_map)->push_back(RPolygon(0, 0, mapxSize, mapySize));
564 t0 = oneapi::tbb::tick_count::now();
565 oneapi::tbb::parallel_for(
566 blocked_range_with_maps<int>(0, (int)(mapxSize + 1), grain_size, polymap1, polymap2),
567 ApplySplitOverlayCV((*result_map), polymap1, polymap2));
568 t1 = oneapi::tbb::tick_count::now();
569 domainSplitParallelTime = (t1 - t0).seconds() * 1000;
570 std::cout << "Splitting parallel with concurrent_vector and ";
571 if (automatic_threadcount)
572 std::cout << "automatic";
573 else
574 std::cout << nthreads;
575 std::cout << ((nthreads == 1) ? " thread" : " threads");
576 std::cout << " took " << domainSplitParallelTime << " msec : speedup over serial "
577 << (gSerialTime / domainSplitParallelTime) << "\n";
578 if (gCsvFile.is_open()) {
579 gCsvFile << "," << domainSplitParallelTime;
580 }
581 #if _DEBUG
582 {
583 Polygon_map_t s_result_map;
584 for (concurrent_Polygon_map_t::const_iterator i = (*result_map)->begin();
585 i != (*result_map)->end();
586 ++i) {
587 s_result_map.push_back(*i);
588 }
589 CheckPolygonMap(&s_result_map);
590 ComparePolygonMaps(&s_result_map, gResultMap);
591 }
592 #endif
593 (*result_map)->clear();
594 }
595
596 if (gCsvFile.is_open()) {
597 gCsvFile << "\n";
598 }
599 }
600
601 // ------------------------------------------------------
602
603 class ApplySplitOverlayETS {
604 Polygon_map_t *m_map1, *m_map2;
605 ETS_Polygon_map_t *m_resultMap;
606
607 public:
608 /*!
609 * @brief functor for columnar parallel version
610 * @param[in] r range of map to be operated on
611 */
operator ()(blocked_range_with_maps<int> & r) const612 void operator()(blocked_range_with_maps<int> &r) const {
613 // get yMapSize
614 int r1, g1, b1, r2, g2, b2;
615 int myr = -1;
616 int myg = -1;
617 int myb = -1;
618 int i1, i2, i3, yMapSize;
619 (*m_map1)[0].get(&i1, &i2, &i3, &yMapSize);
620
621 Flagged_map_t &fmap1 = r.map1();
622 Flagged_map_t &fmap2 = r.map2();
623
624 // When intersecting polygons from fmap1 and fmap2, if BOTH are flagged
625 // as duplicate, don't add the result to the output map. We can still
626 // intersect them, because we are keeping track of how much of the polygon
627 // is left over from intersecting, and quitting when the polygon is
628 // used up.
629
630 for (unsigned int i = 0; i < fmap1.size(); i++) {
631 RPolygon *p1 = fmap1[i].p();
632 bool is_dup = fmap1[i].isDuplicate();
633 int parea = p1->area();
634 p1->getColor(&r1, &g1, &b1);
635 for (unsigned int j = 0; (j < fmap2.size()) && (parea > 0); j++) {
636 int xl, yl, xh, yh;
637 RPolygon *p2 = fmap2[j].p();
638 if (PolygonsOverlap(p1, p2, xl, yl, xh, yh)) {
639 if (!(is_dup && fmap2[j].isDuplicate())) {
640 p2->getColor(&r2, &g2, &b2);
641 myr = r1 + r2;
642 myg = g1 + g2;
643 myb = b1 + b2;
644 (*m_resultMap).local().push_back(RPolygon(xl, yl, xh, yh, myr, myg, myb));
645 }
646 parea -= (xh - xl + 1) * (yh - yl + 1);
647 }
648 }
649 }
650 }
651
ApplySplitOverlayETS(ETS_Polygon_map_t * resultMap,Polygon_map_t * map1,Polygon_map_t * map2)652 ApplySplitOverlayETS(ETS_Polygon_map_t *resultMap, Polygon_map_t *map1, Polygon_map_t *map2)
653 : m_resultMap(resultMap),
654 m_map1(map1),
655 m_map2(map2) {}
656 };
657
658 /*!
659 * @brief intersects two maps strip-wise, accumulating into an ets variable
660 *
661 * @param[out] resultMap output map (must be allocated)
662 * @param[in] polymap1 map to be intersected
663 * @param[in] polymap2 map to be intersected
664 */
SplitParallelOverlayETS(ETS_Polygon_map_t ** result_map,Polygon_map_t * polymap1,Polygon_map_t * polymap2)665 void SplitParallelOverlayETS(ETS_Polygon_map_t **result_map,
666 Polygon_map_t *polymap1,
667 Polygon_map_t *polymap2) {
668 int nthreads;
669 bool automatic_threadcount = false;
670 double domainSplitParallelTime;
671 oneapi::tbb::tick_count t0, t1;
672 if (gThreadsLow == THREADS_UNSET || gThreadsLow == utility::get_default_num_threads()) {
673 gThreadsLow = gThreadsHigh = utility::get_default_num_threads();
674 automatic_threadcount = true;
675 }
676 *result_map = new ETS_Polygon_map_t;
677
678 RPolygon *p0 = &((*polymap1)[0]);
679 int mapxSize, mapySize, ignore1, ignore2;
680 p0->get(&ignore1, &ignore2, &mapxSize, &mapySize);
681 // (*result_map)->reserve(mapxSize*mapySize); // can't be any bigger than this
682
683 int grain_size;
684 #ifdef _DEBUG
685 grain_size = gMapXSize / 4;
686 #else
687 grain_size = gGrainSize;
688 #endif
689 for (nthreads = gThreadsLow; nthreads <= gThreadsHigh; nthreads++) {
690 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
691 nthreads);
692 if (gIsGraphicalVersion) {
693 RPolygon *xp =
694 new RPolygon(0, 0, gMapXSize - 1, gMapYSize - 1, 0, 0, 0); // Clear the output space
695 delete xp;
696 }
697 // push the map size as the first polygon,
698 // This polygon needs to be first, so we can push it at the start of a combine.
699 // (*result_map)->local.push_back(RPolygon(0,0,mapxSize, mapySize));
700 t0 = oneapi::tbb::tick_count::now();
701 oneapi::tbb::parallel_for(
702 blocked_range_with_maps<int>(0, (int)(mapxSize + 1), grain_size, polymap1, polymap2),
703 ApplySplitOverlayETS((*result_map), polymap1, polymap2));
704 t1 = oneapi::tbb::tick_count::now();
705 domainSplitParallelTime = (t1 - t0).seconds() * 1000;
706 std::cout << "Splitting parallel with ETS and ";
707 if (automatic_threadcount)
708 std::cout << "automatic";
709 else
710 std::cout << nthreads;
711 std::cout << ((nthreads == 1) ? " thread" : " threads");
712 std::cout << " took " << domainSplitParallelTime << " msec : speedup over serial "
713 << (gSerialTime / domainSplitParallelTime) << "\n";
714 if (gCsvFile.is_open()) {
715 gCsvFile << "," << domainSplitParallelTime;
716 }
717 #if _DEBUG
718 {
719 Polygon_map_t s_result_map;
720 oneapi::tbb::flattened2d<ETS_Polygon_map_t> psv = flatten2d(**result_map);
721 s_result_map.push_back(RPolygon(0, 0, mapxSize, mapySize));
722 for (oneapi::tbb::flattened2d<ETS_Polygon_map_t>::const_iterator ci = psv.begin();
723 ci != psv.end();
724 ++ci) {
725 s_result_map.push_back(*ci);
726 }
727 CheckPolygonMap(&s_result_map);
728 ComparePolygonMaps(&s_result_map, gResultMap);
729 }
730 #endif
731 (*result_map)->clear();
732 }
733
734 if (gCsvFile.is_open()) {
735 gCsvFile << "\n";
736 }
737 }
738