Adding an internal bound array to LiveViz. This would make it possible to have a...
[charm.git] / src / libs / ck-libs / liveViz / liveViz.C
1 /*
2   liveViz: image-assembly interface.
3
4 Orion Sky Lawlor, olawlor@acm.org, 6/11/2002
5 */
6 #include "liveViz.h"
7 #include "liveViz_impl.h"
8 #include "ImageData.h"
9
10 PUPbytes(liveVizConfig)
11
12 liveVizConfig lv_config;
13 CkReduction::reducerType image_combine_reducer;
14 CProxy_liveVizGroup lvG;
15 CProxy_LiveVizBoundElement lvBoundArray;
16 bool usingBoundArray;
17 CkCallback clientGetImageCallback;
18
19 //Called by clients to start liveViz.
20 void liveVizInit(const liveVizConfig &cfg, CkArrayID a, CkCallback c)
21 {
22   if (CkMyPe()!=0) CkAbort("liveVizInit must be called only on processor 0!");
23   clientGetImageCallback=c;
24   //Broadcast the liveVizConfig object via our group:
25   //  lv_config can't be a readonly because we may be called after
26   //  main::main (because, e.g., communication is needed to find the
27   //  bounding box inside cfg).
28   usingBoundArray = false;
29   lvG = CProxy_liveVizGroup::ckNew(cfg);
30 }
31
32 //Called by clients to start liveViz.
33 void liveVizInit(const liveVizConfig &cfg, CkArrayID a, CkCallback c, CkArrayOptions &opts)
34 {
35   if (CkMyPe()!=0) CkAbort("liveVizInit must be called only on processor 0!");
36   clientGetImageCallback=c;
37   //Broadcast the liveVizConfig object via our group:
38   //  lv_config can't be a readonly because we may be called after
39   //  main::main (because, e.g., communication is needed to find the
40   //  bounding box inside cfg).
41   usingBoundArray = true;
42   CkArrayOptions boundOpts;
43   int dimension = opts.getNumInitial().dimension;
44   switch(dimension){
45   case 1: boundOpts.setNumInitial(opts.getNumInitial().data()[0]); break;
46   case 2: boundOpts.setNumInitial(opts.getNumInitial().data()[0],opts.getNumInitial().data()[1]); break;
47   case 3: boundOpts.setNumInitial(opts.getNumInitial().data()[0],opts.getNumInitial().data()[1],opts.getNumInitial().data()[2]); break;
48   default: CmiAbort("Arrays with more than 3 dimensions are not currently supported by liveViz");
49   }
50   boundOpts.bindTo(a);
51   lvBoundArray = CProxy_LiveVizBoundElement::ckNew(boundOpts);
52   lvG = CProxy_liveVizGroup::ckNew(cfg);
53 }
54
55
56 //Called by reduction handler once every processor has the lv_config object
57 void liveVizInitComplete(void *rednMessage) {
58   delete (CkReductionMsg *)rednMessage;
59   liveViz0Init(lv_config);
60 }
61
62 liveVizRequestMsg *liveVizRequestMsg::buildNew(const liveVizRequest &req,const void *data,int dataLen)
63 {
64         liveVizRequestMsg *m=new (dataLen,0) liveVizRequestMsg();
65         m->req=req;
66         memcpy(m->data,data,dataLen);
67         m->dataLen=dataLen;
68         return m;
69 }
70
71
72 //Called by lower layers when an image request comes in on processor 0.
73 //  Just forwards request on to user.
74 void liveViz0Get(const liveVizRequest &req,void *data,int dataLen)
75 {
76   clientGetImageCallback.send(liveVizRequestMsg::buildNew(req,data,dataLen));
77 }
78
79 /*This array has 512 entries-- it's used to clip off large values
80 when summing bytes (like image values) together.  On a machine with
81 a small cache, it may be better to use an "if" instead of this table.
82 */
83 static byte *overflowArray=CkImage::newClip();
84
85 #if 1
86 /****************** Fast but complex image combining *****************
87
88 Here I create a list of non-overlapping images (lines) and contribute 
89 the list of images rather than the combined images.
90
91 The contributed data looks like:
92   a list of Images (lines) packed into a run of bytes
93 **********************************************************************/
94
95 /// Called by clients to deposit a piece of the final image.
96 ///  Starts the reduction process.
97 void liveVizDeposit(const liveVizRequest &req,
98                     int startx, int starty,
99                     int sizex, int sizey, const byte * src,
100                     ArrayElement* client,
101                     liveVizCombine_t combine)
102 {
103   if (lv_config.getVerbose(2))
104     CkPrintf("liveVizDeposit> Deposited image at (%d,%d), (%d x %d) pixels, on pe %d \n",startx,starty,sizex,sizey,CkMyPe());
105
106   ImageData imageData (lv_config.getBytesPerPixel ());
107   CkReductionMsg* msg = CkReductionMsg::buildNew(imageData.GetBuffSize (startx,
108                                                                         starty,
109                                                                         sizex,
110                                                                         sizey,
111                                                                         req.wid,
112                                                                                                                                                 req.ht,
113                                                                         src),
114                                                  NULL, image_combine_reducer);
115   imageData.WriteHeader(combine,&req,(byte*)(msg->getData()));
116   imageData.AddImage (req.wid, (byte*)(msg->getData()));
117
118   //Contribute this image to the reduction
119   msg->setCallback(CkCallback(vizReductionHandler));
120  
121   if(usingBoundArray){
122     lvBoundArray[client->thisIndexMax].deposit(msg);
123   }else {
124     client->contribute(msg);
125   }
126 }
127
128
129 /**
130 Called by the reduction manager to combine all the source images
131 received on one processor. This function adds all the image on one
132 processor to one list of non-overlapping images.
133 */
134 CkReductionMsg *imageCombineReducer(int nMsg,CkReductionMsg **msgs)
135 {
136   if (nMsg==1) { //Don't bother copying if there's only one source
137     if (lv_config.getVerbose(2))
138       CkPrintf("imageCombine> Skipping combine on pe %d\n",CkMyPe());
139     
140     CkReductionMsg *ret=msgs[0];
141     msgs[0]=NULL; //Prevent reduction manager from double-delete
142     return ret;
143   }
144
145   if (lv_config.getVerbose(2))
146     CkPrintf("imageCombine> image combine on pe %d\n",CkMyPe());
147
148   ImageData imageData (lv_config.getBytesPerPixel ());
149
150   CkReductionMsg* msg = CkReductionMsg::buildNew(imageData.CombineImageDataSize (nMsg,
151                                                                                  msgs),
152                                                  NULL, image_combine_reducer);
153
154   imageData.CombineImageData (nMsg, msgs, (byte*)(msg->getData()));
155   return msg;
156 }
157
158 /* Called once at end of reduction:
159    Unpacks images, combines them to form one image and passes it on to layer 0. */
160 void vizReductionHandler(void *r_msg)
161 {
162   CkReductionMsg *msg = (CkReductionMsg*)r_msg;
163   ImageData imageData (lv_config.getBytesPerPixel ());
164   liveVizRequest req;
165   byte *image = imageData.ConstructImage ((byte*)(msg->getData ()), req);
166   
167   if (lv_config.getVerbose(2))
168       CkPrintf("vizReductionHandler> Assembled image on PE %d \n", CkMyPe());
169   
170   if (lv_config.getBytesPerPixel()!=lv_config.getNetworkBytesPerPixel())
171   { /* Reformat image for the wire: 
172       (only used by floating-point images for now) */
173         int row=req.wid*lv_config.getBytesPerPixel();
174         int netRow=req.wid*lv_config.getNetworkBytesPerPixel();
175         byte *netImage=new byte[req.ht*netRow];
176         liveVizFloatToRGB(req, (float *)image, netImage, req.wid*req.ht);
177         delete[] image;
178         image=netImage;
179   }
180
181   liveViz0Deposit(req,image);
182
183   delete[] image;
184   delete msg;
185 }
186
187 static void liveVizNodeInit(void) {
188   image_combine_reducer=CkReduction::addReducer(imageCombineReducer);
189 }
190
191 #else
192
193 /****************** Fairly Slow, but straightforward image combining *****************
194 The contributed data looks like:
195         liveVizRequest
196         Rectangle of image, in final image coordinates (fully clipped)
197         image data
198
199 This turned out to be incredibly hideous, because the simple data
200 structure above has to be compressed into a flat run of bytes.
201 I'm considering changing the reduction interface to use messages,
202 or some sort of pup'd object.
203 */
204
205
206 CkReductionMsg *allocateImageMsg(const liveVizRequest &req,const CkRect &r,
207         byte **imgDest)
208 {
209   imageHeader hdr(req,r);
210   CkReductionMsg *msg=CkReductionMsg::buildNew(
211         sizeof(imageHeader)+r.area()*lv_config.getBytesPerPixel(),
212         NULL,imageCombineReducer);
213   byte *dest=(byte *)msg->getData();
214   *(imageHeader *)dest=hdr;
215   *imgDest=dest+sizeof(hdr);
216   return msg;
217 }
218
219 //Called by clients to deposit a piece of the final image
220 void liveVizDeposit(const liveVizRequest &req,
221                     int startx, int starty,
222                     int sizex, int sizey, const byte * src,
223                     ArrayElement* client)
224 {
225   if (lv_config.getVerbose(2))
226     CkPrintf("liveVizDeposit> Deposited image at (%d,%d), (%d x %d) pixels, on pe %d\n",
227         startx,starty,sizex,sizey,CkMyPe());
228
229 //Allocate a reductionMessage:
230   CkRect r(startx,starty, startx+sizex,starty+sizey);
231   r=r.getIntersect(CkRect(req.wid,req.ht)); //Never copy out-of-bounds regions
232   if (r.isEmpty()) r.zero();
233   int bpp=lv_config.getBytesPerPixel();
234   byte *dest;
235   CkReductionMsg *msg=allocateImageMsg(req,r,&dest);
236
237 //Copy our image into the reductionMessage:
238   if (!r.isEmpty()) {
239     //We can't just copy image with memcpy, because we may be clipping user data here:
240     CkImage srcImage(sizex,sizey,bpp,(byte *)src);
241     srcImage.window(r.getShift(-startx,-starty)); //Portion of src overlapping dest
242     CkImage destImage(r.wid(),r.ht(),bpp,dest);
243     destImage.put(0,0,srcImage);
244   }
245
246 //Contribute this image to the reduction
247   msg->setCallback(CkCallback(vizReductionHandler));
248
249   client->contribute(msg);
250 }
251
252 /*Called by the reduction manager to combine all the source images
253 received on one processor.
254 */
255 CkReductionMsg *imageCombine(int nMsg,CkReductionMsg **msgs)
256 {
257   if (nMsg==1) { //Don't bother copying if there's only one source
258         if (lv_config.getVerbose(2))
259             CkPrintf("imageCombine> Skipping combine on pe %d\n",CkMyPe());
260         CkReductionMsg *ret=msgs[0];
261         msgs[0]=NULL; //Prevent reduction manager from double-delete
262         return ret;
263   }
264   int m;
265   int bpp=lv_config.getBytesPerPixel();
266   imageHeader *firstHdr=(imageHeader *)msgs[0]->getData();
267
268 //Determine the size of the output image
269   CkRect destRect; destRect.makeEmpty();
270   for (m=0;m<nMsg;m++) destRect=destRect.getUnion(((imageHeader *)msgs[m]->getData())->r);
271
272 //Allocate output message of that size
273   byte *dest;
274   CkReductionMsg *msg=allocateImageMsg(firstHdr->req,destRect,&dest);
275
276 //Add each source image to the destination
277 // Everything should be pre-clippped, so no further geometric clipping is needed.
278 // Brightness clipping, of course, is still necessary.
279   CkImage destImage(destRect.wid(),destRect.ht(),bpp,dest);
280   destImage.clear();
281   for (m=0;m<nMsg;m++) {
282         byte *src=(byte *)msgs[m]->getData();
283         imageHeader *mHdr=(imageHeader *)src;
284         src+=sizeof(imageHeader);
285         if (lv_config.getVerbose(2))
286             CkPrintf("imageCombine>    pe %d  image %d is (%d,%d, %d,%d)\n",
287                   CkMyPe(),m,mHdr->r.l,mHdr->r.t,mHdr->r.r,mHdr->r.b);
288         CkImage srcImage(mHdr->r.wid(),mHdr->r.ht(),bpp,src);
289         destImage.addClip(mHdr->r.l-destRect.l,mHdr->r.t-destRect.t,srcImage,overflowArray);
290   }
291
292   return msg;
293 }
294
295 /*
296 Called once final image has been assembled (reduction handler).
297 Unpacks image, and passes it on to layer 0.
298 */
299 void vizReductionHandler(void *r_msg)
300 {
301   CkReductionMsg *msg = (CkReductionMsg*)r_msg;
302   imageHeader *hdr=(imageHeader *)msg->getData();
303   byte *srcData=sizeof(imageHeader)+(byte *)msg->getData();
304   int bpp=lv_config.getBytesPerPixel();
305   CkRect destRect(0,0,hdr->req.wid,hdr->req.ht);
306   if (destRect==hdr->r) { //Client contributed entire image-- pass along unmodified
307     liveViz0Deposit(hdr->req,srcData);
308   }
309   else
310   { //Client didn't quite cover whole image-- have to pad
311     CkImage src(hdr->r.wid(),hdr->r.ht(),bpp,srcData);
312     CkAllocImage dest(hdr->req.wid,hdr->req.ht,bpp);
313     dest.clear();
314     dest.put(hdr->r.l,hdr->r.t,src);
315     liveViz0Deposit(hdr->req,dest.getData());
316   }
317   delete msg;
318 }
319 #endif
320
321
322 #include "liveViz.def.h"