blob: 99ddfdd303362856a9f302707ce7ea6e1d800dd0 [file] [log] [blame]
/*
Copyright 2005-2010 Intel Corporation. All Rights Reserved.
This file is part of Threading Building Blocks.
Threading Building Blocks is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
Threading Building Blocks is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Threading Building Blocks; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
As a special exception, you may use this file as part of a free software
library without restriction. Specifically, if other files instantiate
templates or use macros or inline functions from this file, or you compile
this file and link it with other files to produce an executable, this
file does not by itself cause the resulting executable to be covered by
the GNU General Public License. This exception does not however
invalidate any other reasons why the executable file might be covered by
the GNU General Public License.
*/
// Polygon overlay
//
#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdlib>
#include <assert.h>
#include "tbb/tick_count.h"
#include "tbb/blocked_range.h"
#include "tbb/task_scheduler_init.h"
#include "tbb/parallel_for.h"
#include "tbb/mutex.h"
#include "tbb/spin_mutex.h"
#include "polyover.h"
#include "polymain.h"
#include "pover_video.h"
using namespace std;
/*!
* @brief intersects a polygon with a map, adding any results to output map
*
* @param[out] resultMap output map (must be allocated)
* @param[in] polygon to be intersected
* @param[in] map intersected against
* @param[in] lock to use when adding output polygons to result map
*
*/
void OverlayOnePolygonWithMap(Polygon_map_t *resultMap, RPolygon *myPoly, Polygon_map_t *map2, tbb::spin_mutex *rMutex) {
int r1, g1, b1, r2, g2, b2;
int myr=0;
int myg=0;
int myb=0;
int p1Area = myPoly->area();
for(unsigned int j=1; (j < map2->size()) && (p1Area > 0); j++) {
RPolygon *p2 = (*map2)[j];
RPolygon *pnew;
int newxMin, newxMax, newyMin, newyMax;
myPoly->getColor(&r1, &g1, &b1);
if(PolygonsOverlap(myPoly, p2, newxMin, newyMin, newxMax, newyMax)) {
p2->getColor(&r2, &g2, &b2);
myr = r1 + r2;
myg = g1 + g2;
myb = b1 + b2;
pnew = RPolygon::alloc_RPolygon(newxMin, newyMin, newxMax, newyMax, myr, myg, myb);
p1Area -= pnew->area(); // when all the area of the polygon is accounted for, we can quit.
if(rMutex) {
tbb::spin_mutex::scoped_lock lock(*rMutex);
#if _DEBUG
pnew->print(int(resultMap->size()));
#endif
resultMap->push_back(pnew);
}
else {
#ifdef _DEBUG
pnew->print(int(resultMap->size()));
#endif
resultMap->push_back(pnew);
}
}
}
}
/*!
* @brief Serial version of polygon overlay
* @param[out] output map
* @param[in] first map (map that individual polygons are taken from)
* @param[in] second map (map passed to OverlayOnePolygonWithMap)
*/
void SerialOverlayMaps(Polygon_map_t **resultMap, Polygon_map_t *map1, Polygon_map_t *map2) {
cout << "SerialOverlayMaps called" << std::endl;
*resultMap = new Polygon_map_t;
RPolygon *p0 = (*map1)[0];
int mapxSize, mapySize, ignore1, ignore2;
p0->get(&ignore1, &ignore2, &mapxSize, &mapySize);
(*resultMap)->reserve(mapxSize*mapySize); // can't be any bigger than this
// push the map size as the first polygon,
p0 = RPolygon::alloc_RPolygon(0,0,mapxSize, mapySize);
(*resultMap)->push_back(p0);
for(unsigned int i=1; i < map1->size(); i++) {
RPolygon *p1 = (*map1)[i];
OverlayOnePolygonWithMap(*resultMap, p1, map2, NULL);
}
}
/*!
* @class ApplyOverlay
* @brief Simple version of parallel overlay (make parallel on polygons in map1)
*/
class ApplyOverlay {
Polygon_map_t *m_map1, *m_map2, *m_resultMap;
tbb::spin_mutex *m_rMutex;
public:
/*!
* @brief functor to apply
* @param[in] r range of polygons to intersect from map1
*/
void operator()( const tbb::blocked_range<int> & r) const {
PRINT_DEBUG("From " << r.begin() << " to " << r.end());
for(int i=r.begin(); i != r.end(); i++) {
RPolygon *myPoly = (*m_map1)[i];
OverlayOnePolygonWithMap(m_resultMap, myPoly, m_map2, m_rMutex);
}
}
ApplyOverlay(Polygon_map_t *resultMap, Polygon_map_t *map1, Polygon_map_t *map2, tbb::spin_mutex *rmutex) :
m_resultMap(resultMap), m_map1(map1), m_map2(map2), m_rMutex(rmutex) {}
};
/*!
* @brief apply the parallel algorithm
* @param[out] result_map generated map
* @param[in] polymap1 first map to be applied (algorithm is parallel on this map)
* @param[in] polymap2 second map.
*/
void NaiveParallelOverlay(Polygon_map_t *&result_map, Polygon_map_t &polymap1, Polygon_map_t &polymap2) {
// -----------------------------------
bool automatic_threadcount = false;
if(gThreadsLow == THREADS_UNSET || gThreadsLow == tbb::task_scheduler_init::automatic) {
gThreadsLow = gThreadsHigh = tbb::task_scheduler_init::automatic;
automatic_threadcount = true;
}
result_map = new Polygon_map_t;
RPolygon *p0 = polymap1[0];
int mapxSize, mapySize, ignore1, ignore2;
p0->get(&ignore1, &ignore2, &mapxSize, &mapySize);
result_map->reserve(mapxSize*mapySize); // can't be any bigger than this
// push the map size as the first polygon,
tbb::spin_mutex *resultMutex = new tbb::spin_mutex();
int grain_size = gGrainSize;
for(int nthreads = gThreadsLow; nthreads <= gThreadsHigh; nthreads++) {
tbb::task_scheduler_init init(nthreads);
if(gIsGraphicalVersion) {
RPolygon *xp = RPolygon::alloc_RPolygon(0, 0, gMapXSize-1, gMapYSize-1, 0, 0, 0); // Clear the output space
RPolygon::free_RPolygon( xp );
}
// put size polygon in result map
p0 = RPolygon::alloc_RPolygon(0,0,mapxSize, mapySize);
result_map->push_back(p0);
tbb::tick_count t0 = tbb::tick_count::now();
tbb::parallel_for (tbb::blocked_range<int>(1,(int)(polymap1.size()),grain_size), ApplyOverlay(result_map, &polymap1, &polymap2, resultMutex));
tbb::tick_count t1 = tbb::tick_count::now();
double naiveParallelTime = (t1-t0).seconds() * 1000;
cout << "Naive parallel with spin lock and ";
if(automatic_threadcount) cout << "automatic";
else cout << nthreads;
cout << ((nthreads == 1) ? " thread" : " threads");
cout << " took " << naiveParallelTime << " msec : speedup over serial " << (gSerialTime / naiveParallelTime) << std::endl;
if(gCsvFile.is_open()) {
gCsvFile << "," << naiveParallelTime;
}
#if _DEBUG
CheckPolygonMap(result_map);
ComparePolygonMaps(result_map, gResultMap);
#endif
for(int i=0; i<int(result_map->size());i++) {
RPolygon::free_RPolygon(result_map->at(i));
}
result_map->clear();
}
delete resultMutex;
if(gCsvFile.is_open()) {
gCsvFile << std::endl;
}
// -----------------------------------
}
/*!
* @class ApplySplitOverlay
* @brief parallel by columnar strip
*/
class ApplySplitOverlay {
Polygon_map_t *m_map1, *m_map2, *m_resultMap;
tbb::spin_mutex *m_rMutex;
public:
/*!
* @brief functor for columnar parallel version
* @param[in] r range of map to be operated on
*/
void operator()(const tbb::blocked_range<int> & r) const {
#ifdef _DEBUG
// if we are debugging, serialize the method. That way we can
// see what is happening in each strip without the interleaving
// confusing things.
tbb::spin_mutex::scoped_lock lock(*m_rMutex);
cout << unitbuf << "From " << r.begin() << " to " << r.end()-1 << std::endl;
#endif
// instead of handing out subsets of polygons from map1 to intersect
// with the polygons in map2, we are handed a strip of the map from
// [(r.begin(),0)-(r.end()-1,yMapSize)].
//
// make a polygon with those values, and intersect with all the polygons
// in map1 and map2, creating flagged polygon lists fmap1 and fmap2.
// There are four possiblities:
//
// 1) a polygon is contained entirely within the strip. We just
// add the polygon to our flagged map.
// 2) the polygon will be partly contained in our strip, and partly
// in the strip to our right (higher x values). Add the polygon
// to our flagged map.
// 3) the polygon is partly contained in our map, and partly in the
// strip to our left. Add the polygon to our map, but flag it as
// a duplicate.
// 4) the polygons do not intersect. Don't add to flagged map.
//
// get yMapSize
int r1, g1, b1, r2, g2, b2;
int myr=-1;
int myg=-1;
int myb=-1;
int i1, i2, i3, yMapSize;
m_map1->at(0)->get(&i1, &i2, &i3, &yMapSize);
RPolygon *slicePolygon = RPolygon::alloc_RPolygon(r.begin(), 0, r.end() - 1, yMapSize);
Flagged_map_t *fmap1, *fmap2;
fmap1 = new std::vector<RPolygon_flagged>;
fmap1->reserve(m_map1->size());
fmap2 = new Flagged_map_t;
fmap2->reserve(m_map2->size());
PRINT_DEBUG(std::endl << "Map1 -------------------");
for(unsigned int i=1; i<m_map1->size(); i++) {
int xl, yl, xh, yh;
RPolygon *px = m_map1->at(i);
if(PolygonsOverlap(slicePolygon, px, xl, yl, xh, yh)) {
bool is_duplicate = false;
int pxl, pyl, pxh, pyh;
int indx = (int)(fmap1->size());
fmap1->resize(indx+1);
fmap1->at(indx).setp(px);
px->get(&pxl, &pyl, &pxh, &pyh);
if(pxl < xl) {
is_duplicate = true;
}
//fmap1->at(indx).setp(px);
fmap1->at(indx).setDuplicate(is_duplicate);
PRINT_DEBUG(" Polygon " << *px << " is in map, is_duplicate=" << is_duplicate);
}
}
PRINT_DEBUG(std::endl << "Map2 -------------------");
for(unsigned int i=1; i<m_map2->size(); i++) {
int xl, yl, xh, yh;
RPolygon *px = m_map2->at(i);
if(PolygonsOverlap(slicePolygon, px, xl, yl, xh, yh)) {
bool is_duplicate = false;
int pxl, pyl, pxh, pyh;
int indx = (int)(fmap2->size());
fmap2->resize(indx+1);
fmap2->at(indx).setp(px);
px->get(&pxl, &pyl, &pxh, &pyh);
if(pxl < xl) {
is_duplicate = true;
}
fmap2->at(indx).setDuplicate(is_duplicate);
PRINT_DEBUG(" Polygon " << *px << " is in map, is_duplicate=" << is_duplicate);
}
}
// When intersecting polygons from fmap1 and fmap2, if BOTH are flagged
// as duplicate, don't add the result to the output map. We can still
// intersect them, because we are keeping track of how much of the polygon
// is left over from intersecting, and quitting when the polygon is
// used up.
for(unsigned int ii=0; ii < fmap1->size(); ii++) {
RPolygon *p1 = fmap1->at(ii).p();
bool is_dup = fmap1->at(ii).isDuplicate();
int parea = p1->area();
p1->getColor(&r1, &g1, &b1);
for(unsigned int jj=0;(jj < fmap2->size()) && (parea > 0); jj++) {
int xl, yl, xh, yh;
RPolygon *p2 = fmap2->at(jj).p();
if(PolygonsOverlap(p1, p2, xl, yl, xh, yh)) {
if(!(is_dup && fmap2->at(jj).isDuplicate())) {
p2->getColor(&r2, &g2, &b2);
myr = r1 + r2;
myg = g1 + g2;
myb = b1 + b2;
RPolygon *pnew = RPolygon::alloc_RPolygon(xl, yl, xh, yh, myr, myg, myb);
#ifdef _DEBUG
#else
tbb::spin_mutex::scoped_lock lock(*m_rMutex);
#endif
(*m_resultMap).push_back(pnew);
}
parea -= (xh-xl+1)*(yh-yl+1);
}
}
}
delete fmap1;
delete fmap2;
RPolygon::free_RPolygon( slicePolygon );
}
ApplySplitOverlay(Polygon_map_t *resultMap, Polygon_map_t *map1, Polygon_map_t *map2, tbb::spin_mutex *rmutex) :
m_resultMap(resultMap), m_map1(map1), m_map2(map2), m_rMutex(rmutex) {}
};
/*!
* @brief intersects two maps strip-wise
*
* @param[out] resultMap output map (must be allocated)
* @param[in] polymap1 map to be intersected
* @param[in] polymap2 map to be intersected
*/
void SplitParallelOverlay(Polygon_map_t **result_map, Polygon_map_t *polymap1, Polygon_map_t *polymap2) {
int nthreads;
bool automatic_threadcount = false;
double domainSplitParallelTime;
tbb::tick_count t0, t1;
tbb::spin_mutex *resultMutex;
if(gThreadsLow == THREADS_UNSET || gThreadsLow == tbb::task_scheduler_init::automatic ) {
gThreadsLow = gThreadsHigh = tbb::task_scheduler_init::automatic;
automatic_threadcount = true;
}
*result_map = new Polygon_map_t;
RPolygon *p0 = (*polymap1)[0];
int mapxSize, mapySize, ignore1, ignore2;
p0->get(&ignore1, &ignore2, &mapxSize, &mapySize);
(*result_map)->reserve(mapxSize*mapySize); // can't be any bigger than this
resultMutex = new tbb::spin_mutex();
int grain_size;
#ifdef _DEBUG
grain_size = gMapXSize / 4;
#else
grain_size = gGrainSize;
#endif
for(nthreads = gThreadsLow; nthreads <= gThreadsHigh; nthreads++) {
tbb::task_scheduler_init init(nthreads);
if(gIsGraphicalVersion) {
RPolygon *xp = RPolygon::alloc_RPolygon(0, 0, gMapXSize-1, gMapYSize-1, 0, 0, 0); // Clear the output space
RPolygon::free_RPolygon( xp );
}
// push the map size as the first polygon,
p0 = RPolygon::alloc_RPolygon(0,0,mapxSize, mapySize);
(*result_map)->push_back(p0);
t0 = tbb::tick_count::now();
tbb::parallel_for (tbb::blocked_range<int>(0,(int)(mapxSize+1),grain_size), ApplySplitOverlay((*result_map), polymap1, polymap2, resultMutex));
t1 = tbb::tick_count::now();
domainSplitParallelTime = (t1-t0).seconds()*1000;
cout << "Splitting parallel with spin lock and ";
if(automatic_threadcount) cout << "automatic";
else cout << nthreads;
cout << ((nthreads == 1) ? " thread" : " threads");
cout << " took " << domainSplitParallelTime << " msec : speedup over serial " << (gSerialTime / domainSplitParallelTime) << std::endl;
if(gCsvFile.is_open()) {
gCsvFile << "," << domainSplitParallelTime;
}
#if _DEBUG
CheckPolygonMap(*result_map);
ComparePolygonMaps(*result_map, gResultMap);
#endif
for(int i=0; i<int((*result_map)->size());i++) {
RPolygon::free_RPolygon((*result_map)->at(i));
}
(*result_map)->clear();
}
delete resultMutex;
if(gCsvFile.is_open()) {
gCsvFile << std::endl;
}
}