PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/OpenEXR/IlmImfTest/testOptimizedInterleavePatterns.cpp

https://github.com/meshula/openexr
C++ | 565 lines | 433 code | 86 blank | 46 comment | 33 complexity | fee590d622a31aa9bbf3f689bb6140a5 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, BSD-3-Clause
  1. ///////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2013, Weta Digital Ltd
  4. //
  5. // All rights reserved.
  6. //
  7. // Redistribution and use in source and binary forms, with or without
  8. // modification, are permitted provided that the following conditions are
  9. // met:
  10. // * Redistributions of source code must retain the above copyright
  11. // notice, this list of conditions and the following disclaimer.
  12. // * Redistributions in binary form must reproduce the above
  13. // copyright notice, this list of conditions and the following disclaimer
  14. // in the documentation and/or other materials provided with the
  15. // distribution.
  16. // * Neither the name of Industrial Light & Magic nor the names of
  17. // its contributors may be used to endorse or promote products derived
  18. // from this software without specific prior written permission.
  19. //
  20. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. //
  32. ///////////////////////////////////////////////////////////////////////////
  33. #include "ImfInputFile.h"
  34. #include <stdlib.h>
  35. #include <vector>
  36. #include "ImfChannelList.h"
  37. #include "ImfOutputFile.h"
  38. #include "ImfCompression.h"
  39. #include "ImfStandardAttributes.h"
  40. #include <algorithm>
  41. #include <iostream>
  42. #include <assert.h>
  43. #include <IlmThread.h>
  44. #include <ImathBox.h>
  45. #include "tmpDir.h"
  46. using namespace OPENEXR_IMF_NAMESPACE;
  47. using namespace std;
  48. using namespace IMATH_NAMESPACE;
  49. using namespace ILMTHREAD_NAMESPACE;
  50. namespace
  51. {
  52. const char filename[] = IMF_TMP_DIR "imf_test_interleave_patterns.exr";
  53. vector<char> writingBuffer; // buffer as file was written
  54. vector<char> readingBuffer; // buffer containing new image (and filled channels?)
  55. vector<char> preReadBuffer; // buffer as it was before reading - unread, unfilled channels should be unchanged
  56. int gOptimisedReads = 0;
  57. int gSuccesses = 0;
  58. int gFailures = 0;
  59. struct Schema
  60. {
  61. const char* _name; // name of this scheme
  62. const char* const* _active; // channels to be read
  63. const char* const * _passive; // channels to be ignored (keep in buffer passed to inputfile, should not be overwritten)
  64. int _banks;
  65. const char* const *_views; // list of views to write, or NULL
  66. const PixelType* _types; // NULL for all HALF, otherwise per-channel type
  67. vector<string> views() const
  68. {
  69. const char * const* v = _views;
  70. vector<string> svec;
  71. while(*v!=NULL)
  72. {
  73. svec.push_back(*v);
  74. v++;
  75. }
  76. return svec;
  77. }
  78. };
  79. const char * rgb[] = {"R","G","B",NULL};
  80. const char * rgba[] = {"R","G","B","A",NULL};
  81. const char * bgr[] = {"B","G","R",NULL};
  82. const char * abgr[] = {"A","B","G","R",NULL};
  83. const char * alpha[] = {"A",NULL};
  84. const char * redalpha[] = {"R","A",NULL};
  85. const char * rgbrightrgb[] = {"R","G","B","right.R","right.G","right.B",NULL};
  86. const char * rgbleftrgb[] = {"R","G","B","left.R","left.G","left.B",NULL};
  87. const char * rgbarightrgba[] = {"R","G","B","A","right.R","right.G","right.B","right.A",NULL};
  88. const char * rgbaleftrgba[] = {"R","G","B","A","left.R","left.G","left.B","left.A",NULL};
  89. const char * rgbrightrgba[] = {"R","G","B","right.R","right.G","right.B","right.A",NULL};
  90. const char * rgbleftrgba[] = {"R","G","B","left.R","left.G","left.B","left.A",NULL};
  91. const char * rgbarightrgb[] = {"R","G","B","A","right.R","right.G","right.B",NULL};
  92. const char * rgbaleftrgb[] = {"R","G","B","A","left.R","left.G","left.B",NULL};
  93. const char * rightrgba[] = {"right.R","right.G","right.B","right.A",NULL};
  94. const char * leftrgba[] = {"left.R","left.G","left.B","left.A",NULL};
  95. const char * rightrgb[] = {"right.R","right.G","right.B",NULL};
  96. const char * leftrgb[] = {"left.R","left.G","left.B",NULL};
  97. const char * threeview[] ={"R","G","B","A","left.R","left.G","left.B","left.A","right.R","right.G","right.B","right.A",NULL};
  98. const char * trees[] = {"rimu","pohutukawa","manuka","kauri",NULL};
  99. const char * treesandbirds[]= {"kiwi","rimu","pohutukawa","kakapu","kauri","manuka","moa","fantail",NULL};
  100. const char * lefthero[] = {"left","right",NULL};
  101. const char * righthero[] = {"right","left",NULL};
  102. const char * centrehero[] = {"centre","left","right",NULL};
  103. const PixelType four_floats[] = {FLOAT,FLOAT,FLOAT,FLOAT};
  104. const PixelType hhhfff[] = {HALF,HALF,HALF,FLOAT,FLOAT,FLOAT};
  105. const PixelType hhhhffff[] = {HALF,HALF,HALF,HALF,FLOAT,FLOAT,FLOAT,FLOAT};
  106. Schema Schemes[] = {
  107. {"RGBHalf" ,rgb ,NULL,1,NULL ,NULL},
  108. {"RGBAHalf" ,rgba ,NULL,1,NULL ,NULL},
  109. {"ABGRHalf" ,abgr ,NULL,1,NULL ,NULL},
  110. {"RGBFloat" ,rgb ,NULL,1,NULL ,four_floats},
  111. {"BGRHalf" ,bgr ,NULL,1,NULL ,NULL},
  112. {"RGBLeftRGB" ,rgbleftrgb ,NULL,1,righthero ,NULL},
  113. {"RGBRightRGB" ,rgbrightrgb ,NULL,1,lefthero ,NULL},
  114. {"RGBALeftRGBA" ,rgbaleftrgba ,NULL,1,righthero ,NULL},
  115. {"RGBARightRGBA",rgbarightrgba ,NULL,1,lefthero ,NULL},
  116. {"LeftRGB" ,leftrgb ,NULL,1,NULL ,NULL},
  117. {"RightRGB" ,rightrgb ,NULL,1,NULL ,NULL},
  118. {"LeftRGBA" ,leftrgba ,NULL,1,NULL ,NULL},
  119. {"RightRGBA" ,rightrgba ,NULL,1,NULL ,NULL},
  120. {"TripleView" ,threeview ,NULL,1,centrehero ,NULL},
  121. {"Trees" ,trees ,NULL,1,NULL ,NULL},
  122. {"TreesAndBirds",treesandbirds ,NULL,1,NULL ,NULL},
  123. {"RGBLeftRGBA" ,rgbleftrgba ,NULL,1,righthero ,NULL},
  124. {"RGBRightRGBA" ,rgbrightrgba ,NULL,1,lefthero ,NULL},
  125. {"RGBALeftRGB" ,rgbaleftrgb ,NULL,1,righthero ,NULL},
  126. {"RGBARightRGB" ,rgbarightrgb ,NULL,1,lefthero ,NULL},
  127. {"TwinRGBLeftRGB" ,rgbleftrgb ,NULL,2,righthero ,NULL},
  128. {"TwinRGBRightRGB" ,rgbrightrgb ,NULL,2,lefthero ,NULL},
  129. {"TwinRGBALeftRGBA" ,rgbaleftrgba ,NULL,2,righthero ,NULL},
  130. {"TwinRGBARightRGBA",rgbarightrgba ,NULL,2,lefthero ,NULL},
  131. {"TripleTripleView" ,threeview ,NULL,3,centrehero ,NULL},
  132. {"Alpha" ,alpha ,NULL,1,NULL ,NULL},
  133. {"RedAlpha" ,redalpha ,NULL,1,NULL ,NULL},
  134. {"RG+BA" ,rgba ,NULL,2,NULL ,NULL},//interleave only RG, then BA
  135. {"RGBpassiveA" ,rgb ,alpha,1,NULL ,NULL},//interleave only RG, then BA
  136. {"RGBpassiveleftRGB" ,rgb ,leftrgb,1,NULL ,NULL},
  137. {"RGBFloatA" ,rgba ,NULL,1,NULL ,hhhfff},
  138. {"RGBFloatLeftRGB" ,rgbleftrgb ,NULL,1,righthero ,hhhfff},
  139. {"RGBAFloatLeftRGBA" ,rgbaleftrgba,NULL,1,righthero,hhhhffff},
  140. {"RGBApassiverightRGBA",rgba ,rightrgba,1,NULL ,NULL},
  141. {"BanksOfTreesAndBirds",treesandbirds ,NULL,2,NULL ,NULL},
  142. {NULL,NULL,NULL,0,NULL,NULL}
  143. };
  144. bool compare(const FrameBuffer& asRead,
  145. const FrameBuffer& asWritten,
  146. const Box2i& dataWindow,
  147. bool nonfatal
  148. )
  149. {
  150. for (FrameBuffer::ConstIterator i =asRead.begin();i!=asRead.end();i++)
  151. {
  152. FrameBuffer::ConstIterator p = asWritten.find(i.name());
  153. for (int y=dataWindow.min.y;
  154. y<= dataWindow.max.y;
  155. y++)
  156. {
  157. for (int x = dataWindow.min.x;
  158. x <= dataWindow.max.x;
  159. x++)
  160. {
  161. char * ptr =
  162. (i.slice().base+i.slice().yStride*y +i.slice().xStride*x);
  163. half readHalf;
  164. switch(i.slice().type)
  165. {
  166. case FLOAT :
  167. readHalf = half(*(float*) ptr);
  168. break;
  169. case HALF :
  170. readHalf = half(*(half*) ptr);
  171. break;
  172. case UINT :
  173. continue; // can't very well check this
  174. default :
  175. cout << "don't know about that\n";
  176. exit(1);
  177. }
  178. half writtenHalf;
  179. if (p!=asWritten.end())
  180. {
  181. char * ptr = p.slice().base+p.slice().yStride*y +
  182. p.slice().xStride*x;
  183. switch (p.slice().type)
  184. {
  185. case FLOAT :
  186. writtenHalf = half(*(float*) ptr);
  187. break;
  188. case HALF :
  189. writtenHalf = half(*(half*) ptr);
  190. break;
  191. case UINT :
  192. continue;
  193. default :
  194. cout << "don't know about that\n";
  195. exit(1);
  196. }
  197. } else {
  198. writtenHalf=half(i.slice().fillValue);
  199. }
  200. if(writtenHalf.bits()!=readHalf.bits())
  201. {
  202. if(nonfatal)
  203. {
  204. return false;
  205. }else{
  206. cout << "\n\nerror reading back channel " << i.name() << " pixel " << x << ',' << y << " got " << readHalf << " expected " << writtenHalf << endl;
  207. assert(writtenHalf.bits()==readHalf.bits());
  208. exit(1);
  209. }
  210. }
  211. }
  212. }
  213. }
  214. return true;
  215. }
  216. //
  217. // allocate readingBuffer or writingBuffer, setting up a framebuffer to point to the right thing
  218. //
  219. ChannelList setupBuffer(const Header& hdr, // header to grab datawindow from
  220. const char * const *channels, // NULL terminated list of channels to write
  221. const char * const *passivechannels, // NULL terminated list of channels to write
  222. const PixelType* pt, // type of each channel, or NULL for all HALF
  223. FrameBuffer& buf, // buffer to fill with pointers to channel
  224. FrameBuffer& prereadbuf, // channels which aren't being read - indexes into the preread buffer
  225. FrameBuffer& postreadbuf, // channels which aren't being read - indexes into the postread buffer
  226. int banks, // number of banks - channels within each bank are interleaved, banks are scanline interleaved
  227. bool writing // true if should allocate
  228. )
  229. {
  230. Box2i dw = hdr.dataWindow();
  231. //
  232. // how many channels in total
  233. //
  234. int activechans = 0;
  235. int bytes_per_pixel =0;
  236. while (channels[activechans]!=NULL)
  237. {
  238. if(pt==NULL)
  239. {
  240. bytes_per_pixel+=2;
  241. }else{
  242. switch(pt[activechans])
  243. {
  244. case HALF : bytes_per_pixel+=2;break;
  245. case FLOAT : case UINT : bytes_per_pixel+=4;break;
  246. default :
  247. cout << "wot?\n";
  248. exit(1);
  249. }
  250. }
  251. activechans++;
  252. }
  253. int passivechans=0;
  254. while (passivechannels!=NULL && passivechannels[passivechans]!=NULL)
  255. {
  256. if(pt==NULL)
  257. {
  258. bytes_per_pixel+=2;
  259. }else{
  260. switch(pt[passivechans+activechans])
  261. {
  262. case HALF : bytes_per_pixel+=2;break;
  263. case FLOAT : case UINT : bytes_per_pixel+=4;break;
  264. default :
  265. cout << "wot?\n";
  266. exit(1);
  267. }
  268. }
  269. passivechans++;
  270. }
  271. int chans = activechans+passivechans;
  272. int bytes_per_bank = bytes_per_pixel/banks;
  273. int samples = (hdr.dataWindow().max.x+1-hdr.dataWindow().min.x)*
  274. (hdr.dataWindow().max.y+1-hdr.dataWindow().min.y)*chans;
  275. int size = samples*bytes_per_pixel;
  276. if(writing)
  277. {
  278. writingBuffer.resize(size);
  279. }else{
  280. readingBuffer.resize(size);
  281. }
  282. const char * write_ptr = writing ? &writingBuffer[0] : &readingBuffer[0];
  283. // fill with random halfs, casting to floats for float channels
  284. int chan=0;
  285. for (int i=0;i<samples;i++)
  286. {
  287. unsigned short int values = (unsigned short int) floor((double(rand())/double(RAND_MAX))*65535.0);
  288. half v;
  289. v.setBits(values);
  290. if (pt==NULL || pt[chan]==HALF)
  291. {
  292. *(half*)write_ptr = half(v);
  293. write_ptr+=2;
  294. } else {
  295. *(float*)write_ptr = float(v);
  296. write_ptr+=4;
  297. }
  298. chan++;
  299. if(chan==chans)
  300. {
  301. chan=0;
  302. }
  303. }
  304. if(!writing)
  305. {
  306. //take a copy of the buffer as it was before being read
  307. preReadBuffer = readingBuffer;
  308. }
  309. char* offset=NULL;
  310. ChannelList chanlist;
  311. int bytes_per_row = bytes_per_pixel*(dw.max.x+1-dw.min.x);
  312. int bytes_per_bank_row = bytes_per_row/banks;
  313. int first_pixel_index = bytes_per_row*dw.min.y+bytes_per_bank*dw.min.x;
  314. for (int i=0;i<chans;i++)
  315. {
  316. PixelType type = pt==NULL ? HALF : pt[i];
  317. if (i<activechans && writing)
  318. {
  319. chanlist.insert(channels[i],type);
  320. }
  321. if(i % (chans/banks) ==0)
  322. {
  323. //
  324. // set offset pointer to beginning of bank
  325. //
  326. int bank = i / (chans/banks);
  327. offset = (writing ? &writingBuffer[0] : &readingBuffer[0]) + bank*bytes_per_bank_row - first_pixel_index;
  328. }
  329. if(i<activechans)
  330. {
  331. buf.insert(channels[i],
  332. Slice(type,offset,bytes_per_bank,
  333. bytes_per_row,1,1,100+i));
  334. }else{
  335. if(!writing)
  336. {
  337. postreadbuf.insert(passivechannels[i-activechans],
  338. Slice(type,offset,bytes_per_bank,
  339. bytes_per_row,1,1,0.4));
  340. char * pre_offset = offset-&readingBuffer[0]+&preReadBuffer[0];
  341. prereadbuf.insert(passivechannels[i-activechans],
  342. Slice(type, pre_offset,bytes_per_bank,
  343. bytes_per_row,1,1,0.4));
  344. }
  345. }
  346. switch (type)
  347. {
  348. case HALF :
  349. offset+=2;
  350. break;
  351. case FLOAT :
  352. offset+=4;
  353. break;
  354. default :
  355. cout << "don't know about that\n";
  356. exit(1);
  357. }
  358. }
  359. return chanlist;
  360. }
  361. Box2i writefile(Schema & scheme,FrameBuffer& buf,bool tiny)
  362. {
  363. const int height = 128;
  364. const int width = 128;
  365. Header hdr(width,height,1);
  366. //min values in range (-100,100)
  367. hdr.dataWindow().min.x = int(200.0*double(rand())/double(RAND_MAX)-100.0);
  368. hdr.dataWindow().min.y = int(200.0*double(rand())/double(RAND_MAX)-100.0);
  369. // in tiny mode, make image up to 14*14 pixels (less than two SSE instructions)
  370. if(tiny)
  371. {
  372. hdr.dataWindow().max.x = hdr.dataWindow().min.x + 1+int(13*double(rand())/double(RAND_MAX));
  373. hdr.dataWindow().max.y = hdr.dataWindow().min.y + 1+int(13*double(rand())/double(RAND_MAX));
  374. }else{
  375. // in normal mode, make chunky images
  376. hdr.dataWindow().max.x = hdr.dataWindow().min.x + 64+int(400*double(rand())/double(RAND_MAX));
  377. hdr.dataWindow().max.y = hdr.dataWindow().min.y + 64+int(400*double(rand())/double(RAND_MAX));
  378. }
  379. hdr.compression()=ZIPS_COMPRESSION;
  380. FrameBuffer dummy1,dummy2;
  381. hdr.channels() = setupBuffer(hdr,scheme._active,scheme._passive,scheme._types,buf,dummy1,dummy2,scheme._banks,true);
  382. if(scheme._views)
  383. {
  384. addMultiView(hdr,scheme.views());
  385. }
  386. remove(filename);
  387. OutputFile f(filename,hdr);
  388. f.setFrameBuffer(buf);
  389. f.writePixels(hdr.dataWindow().max.y-hdr.dataWindow().min.y+1);
  390. return hdr.dataWindow();
  391. }
  392. bool readfile(Schema scheme,
  393. FrameBuffer & buf, ///< list of channels to read: index to readingBuffer
  394. FrameBuffer & preread,///< list of channels to skip: index to preReadBuffer
  395. FrameBuffer & postread)///< list of channels to skip: index to readingBuffer)
  396. {
  397. InputFile infile(filename);
  398. setupBuffer(infile.header(),scheme._active,scheme._passive, scheme._types,buf,preread,postread,scheme._banks,false);
  399. infile.setFrameBuffer(buf);
  400. cout.flush();
  401. infile.readPixels(infile.header().dataWindow().min.y,infile.header().dataWindow().max.y);
  402. return infile.isOptimizationEnabled();
  403. }
  404. void test(Schema writeScheme,Schema readScheme,bool nonfatal,bool tiny)
  405. {
  406. ostringstream q;
  407. q << writeScheme._name << " read as " << readScheme._name << "...";
  408. cout << left << setw(53) << q.str();
  409. FrameBuffer writeFrameBuf;
  410. Box2i dw = writefile(writeScheme,writeFrameBuf,tiny);
  411. FrameBuffer readFrameBuf;
  412. FrameBuffer preReadFrameBuf;
  413. FrameBuffer postReadFrameBuf;
  414. cout.flush();
  415. bool opt = readfile(readScheme,readFrameBuf,preReadFrameBuf,postReadFrameBuf);
  416. if(compare(readFrameBuf,writeFrameBuf,dw,nonfatal) &&
  417. compare(preReadFrameBuf,postReadFrameBuf,dw,nonfatal)
  418. )
  419. {
  420. cout << " OK ";
  421. if(opt)
  422. {
  423. cout << "OPTIMISED ";
  424. gOptimisedReads++;
  425. }
  426. cout << "\n";
  427. gSuccesses++;
  428. }else{
  429. cout << " FAIL" << endl;
  430. gFailures++;
  431. }
  432. remove(filename);
  433. }
  434. void runtests(bool nonfatal,bool tiny)
  435. {
  436. srand(1);
  437. int i=0;
  438. int skipped=0;
  439. gFailures=0;
  440. gSuccesses=0;
  441. gOptimisedReads=0;
  442. while(Schemes[i]._name!=NULL)
  443. {
  444. int j=0;
  445. while(Schemes[j]._name!=NULL)
  446. {
  447. cout << right << setw(2) << i << ',' << right << setw(2) << j << ": ";
  448. cout.flush();
  449. if(nonfatal)
  450. {
  451. cout << " skipping " << Schemes[i]._name << ',' << Schemes[j]._name << ": known to crash\n";
  452. skipped++;
  453. }
  454. else
  455. {
  456. test(Schemes[i],Schemes[j],nonfatal,tiny);
  457. }
  458. j++;
  459. }
  460. i++;
  461. }
  462. cout << gFailures << '/' << (gSuccesses+gFailures) << " runs failed\n";
  463. cout << skipped << " tests skipped (assumed to be bad)\n";
  464. cout << gOptimisedReads << '/' << gSuccesses << " optimised\n";
  465. if(gFailures>0 )
  466. {
  467. cout << " TESTS FAILED\n";
  468. assert(false);
  469. }
  470. }
  471. }
  472. void testOptimizedInterleavePatterns()
  473. {
  474. cout << "Testing SSE optimisation with different interleave patterns (large images) ... " << endl;
  475. runtests(false,false);
  476. cout << "Testing SSE optimisation with different interleave patterns (tiny images) ... " << endl;
  477. runtests(false,true);
  478. cout << "ok\n" << endl;
  479. }