/OpenEXR/IlmImfTest/testOptimizedInterleavePatterns.cpp
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
- ///////////////////////////////////////////////////////////////////////////
- //
- // Copyright (c) 2013, Weta Digital Ltd
- //
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Industrial Light & Magic nor the names of
- // its contributors may be used to endorse or promote products derived
- // from this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- //
- ///////////////////////////////////////////////////////////////////////////
- #include "ImfInputFile.h"
- #include <stdlib.h>
- #include <vector>
- #include "ImfChannelList.h"
- #include "ImfOutputFile.h"
- #include "ImfCompression.h"
- #include "ImfStandardAttributes.h"
- #include <algorithm>
- #include <iostream>
- #include <assert.h>
- #include <IlmThread.h>
- #include <ImathBox.h>
- #include "tmpDir.h"
- using namespace OPENEXR_IMF_NAMESPACE;
- using namespace std;
- using namespace IMATH_NAMESPACE;
- using namespace ILMTHREAD_NAMESPACE;
- namespace
- {
-
- const char filename[] = IMF_TMP_DIR "imf_test_interleave_patterns.exr";
- vector<char> writingBuffer; // buffer as file was written
- vector<char> readingBuffer; // buffer containing new image (and filled channels?)
- vector<char> preReadBuffer; // buffer as it was before reading - unread, unfilled channels should be unchanged
- int gOptimisedReads = 0;
- int gSuccesses = 0;
- int gFailures = 0;
- struct Schema
- {
- const char* _name; // name of this scheme
- const char* const* _active; // channels to be read
- const char* const * _passive; // channels to be ignored (keep in buffer passed to inputfile, should not be overwritten)
- int _banks;
- const char* const *_views; // list of views to write, or NULL
- const PixelType* _types; // NULL for all HALF, otherwise per-channel type
-
- vector<string> views() const
- {
- const char * const* v = _views;
- vector<string> svec;
- while(*v!=NULL)
- {
- svec.push_back(*v);
- v++;
- }
- return svec;
- }
- };
- const char * rgb[] = {"R","G","B",NULL};
- const char * rgba[] = {"R","G","B","A",NULL};
- const char * bgr[] = {"B","G","R",NULL};
- const char * abgr[] = {"A","B","G","R",NULL};
- const char * alpha[] = {"A",NULL};
- const char * redalpha[] = {"R","A",NULL};
- const char * rgbrightrgb[] = {"R","G","B","right.R","right.G","right.B",NULL};
- const char * rgbleftrgb[] = {"R","G","B","left.R","left.G","left.B",NULL};
- const char * rgbarightrgba[] = {"R","G","B","A","right.R","right.G","right.B","right.A",NULL};
- const char * rgbaleftrgba[] = {"R","G","B","A","left.R","left.G","left.B","left.A",NULL};
- const char * rgbrightrgba[] = {"R","G","B","right.R","right.G","right.B","right.A",NULL};
- const char * rgbleftrgba[] = {"R","G","B","left.R","left.G","left.B","left.A",NULL};
- const char * rgbarightrgb[] = {"R","G","B","A","right.R","right.G","right.B",NULL};
- const char * rgbaleftrgb[] = {"R","G","B","A","left.R","left.G","left.B",NULL};
- const char * rightrgba[] = {"right.R","right.G","right.B","right.A",NULL};
- const char * leftrgba[] = {"left.R","left.G","left.B","left.A",NULL};
- const char * rightrgb[] = {"right.R","right.G","right.B",NULL};
- const char * leftrgb[] = {"left.R","left.G","left.B",NULL};
- const char * threeview[] ={"R","G","B","A","left.R","left.G","left.B","left.A","right.R","right.G","right.B","right.A",NULL};
- const char * trees[] = {"rimu","pohutukawa","manuka","kauri",NULL};
- const char * treesandbirds[]= {"kiwi","rimu","pohutukawa","kakapu","kauri","manuka","moa","fantail",NULL};
- const char * lefthero[] = {"left","right",NULL};
- const char * righthero[] = {"right","left",NULL};
- const char * centrehero[] = {"centre","left","right",NULL};
- const PixelType four_floats[] = {FLOAT,FLOAT,FLOAT,FLOAT};
- const PixelType hhhfff[] = {HALF,HALF,HALF,FLOAT,FLOAT,FLOAT};
- const PixelType hhhhffff[] = {HALF,HALF,HALF,HALF,FLOAT,FLOAT,FLOAT,FLOAT};
- Schema Schemes[] = {
- {"RGBHalf" ,rgb ,NULL,1,NULL ,NULL},
- {"RGBAHalf" ,rgba ,NULL,1,NULL ,NULL},
- {"ABGRHalf" ,abgr ,NULL,1,NULL ,NULL},
- {"RGBFloat" ,rgb ,NULL,1,NULL ,four_floats},
- {"BGRHalf" ,bgr ,NULL,1,NULL ,NULL},
- {"RGBLeftRGB" ,rgbleftrgb ,NULL,1,righthero ,NULL},
- {"RGBRightRGB" ,rgbrightrgb ,NULL,1,lefthero ,NULL},
- {"RGBALeftRGBA" ,rgbaleftrgba ,NULL,1,righthero ,NULL},
- {"RGBARightRGBA",rgbarightrgba ,NULL,1,lefthero ,NULL},
- {"LeftRGB" ,leftrgb ,NULL,1,NULL ,NULL},
- {"RightRGB" ,rightrgb ,NULL,1,NULL ,NULL},
- {"LeftRGBA" ,leftrgba ,NULL,1,NULL ,NULL},
- {"RightRGBA" ,rightrgba ,NULL,1,NULL ,NULL},
- {"TripleView" ,threeview ,NULL,1,centrehero ,NULL},
- {"Trees" ,trees ,NULL,1,NULL ,NULL},
- {"TreesAndBirds",treesandbirds ,NULL,1,NULL ,NULL},
- {"RGBLeftRGBA" ,rgbleftrgba ,NULL,1,righthero ,NULL},
- {"RGBRightRGBA" ,rgbrightrgba ,NULL,1,lefthero ,NULL},
- {"RGBALeftRGB" ,rgbaleftrgb ,NULL,1,righthero ,NULL},
- {"RGBARightRGB" ,rgbarightrgb ,NULL,1,lefthero ,NULL},
- {"TwinRGBLeftRGB" ,rgbleftrgb ,NULL,2,righthero ,NULL},
- {"TwinRGBRightRGB" ,rgbrightrgb ,NULL,2,lefthero ,NULL},
- {"TwinRGBALeftRGBA" ,rgbaleftrgba ,NULL,2,righthero ,NULL},
- {"TwinRGBARightRGBA",rgbarightrgba ,NULL,2,lefthero ,NULL},
- {"TripleTripleView" ,threeview ,NULL,3,centrehero ,NULL},
- {"Alpha" ,alpha ,NULL,1,NULL ,NULL},
- {"RedAlpha" ,redalpha ,NULL,1,NULL ,NULL},
- {"RG+BA" ,rgba ,NULL,2,NULL ,NULL},//interleave only RG, then BA
- {"RGBpassiveA" ,rgb ,alpha,1,NULL ,NULL},//interleave only RG, then BA
- {"RGBpassiveleftRGB" ,rgb ,leftrgb,1,NULL ,NULL},
- {"RGBFloatA" ,rgba ,NULL,1,NULL ,hhhfff},
- {"RGBFloatLeftRGB" ,rgbleftrgb ,NULL,1,righthero ,hhhfff},
- {"RGBAFloatLeftRGBA" ,rgbaleftrgba,NULL,1,righthero,hhhhffff},
- {"RGBApassiverightRGBA",rgba ,rightrgba,1,NULL ,NULL},
- {"BanksOfTreesAndBirds",treesandbirds ,NULL,2,NULL ,NULL},
- {NULL,NULL,NULL,0,NULL,NULL}
- };
- bool compare(const FrameBuffer& asRead,
- const FrameBuffer& asWritten,
- const Box2i& dataWindow,
- bool nonfatal
- )
- {
- for (FrameBuffer::ConstIterator i =asRead.begin();i!=asRead.end();i++)
- {
- FrameBuffer::ConstIterator p = asWritten.find(i.name());
- for (int y=dataWindow.min.y;
- y<= dataWindow.max.y;
- y++)
- {
- for (int x = dataWindow.min.x;
- x <= dataWindow.max.x;
- x++)
-
- {
- char * ptr =
- (i.slice().base+i.slice().yStride*y +i.slice().xStride*x);
- half readHalf;
- switch(i.slice().type)
- {
- case FLOAT :
- readHalf = half(*(float*) ptr);
- break;
- case HALF :
- readHalf = half(*(half*) ptr);
- break;
- case UINT :
- continue; // can't very well check this
- default :
- cout << "don't know about that\n";
- exit(1);
- }
-
- half writtenHalf;
- if (p!=asWritten.end())
- {
- char * ptr = p.slice().base+p.slice().yStride*y +
- p.slice().xStride*x;
- switch (p.slice().type)
- {
- case FLOAT :
- writtenHalf = half(*(float*) ptr);
- break;
- case HALF :
- writtenHalf = half(*(half*) ptr);
- break;
- case UINT :
- continue;
- default :
- cout << "don't know about that\n";
- exit(1);
- }
- } else {
- writtenHalf=half(i.slice().fillValue);
- }
- if(writtenHalf.bits()!=readHalf.bits())
- {
- if(nonfatal)
- {
- return false;
- }else{
- cout << "\n\nerror reading back channel " << i.name() << " pixel " << x << ',' << y << " got " << readHalf << " expected " << writtenHalf << endl;
- assert(writtenHalf.bits()==readHalf.bits());
- exit(1);
- }
- }
- }
- }
- }
- return true;
- }
- //
- // allocate readingBuffer or writingBuffer, setting up a framebuffer to point to the right thing
- //
- ChannelList setupBuffer(const Header& hdr, // header to grab datawindow from
- const char * const *channels, // NULL terminated list of channels to write
- const char * const *passivechannels, // NULL terminated list of channels to write
- const PixelType* pt, // type of each channel, or NULL for all HALF
- FrameBuffer& buf, // buffer to fill with pointers to channel
- FrameBuffer& prereadbuf, // channels which aren't being read - indexes into the preread buffer
- FrameBuffer& postreadbuf, // channels which aren't being read - indexes into the postread buffer
- int banks, // number of banks - channels within each bank are interleaved, banks are scanline interleaved
- bool writing // true if should allocate
- )
- {
- Box2i dw = hdr.dataWindow();
- //
- // how many channels in total
- //
- int activechans = 0;
- int bytes_per_pixel =0;
-
- while (channels[activechans]!=NULL)
- {
- if(pt==NULL)
- {
- bytes_per_pixel+=2;
- }else{
- switch(pt[activechans])
- {
- case HALF : bytes_per_pixel+=2;break;
- case FLOAT : case UINT : bytes_per_pixel+=4;break;
- default :
- cout << "wot?\n";
- exit(1);
- }
- }
- activechans++;
- }
-
- int passivechans=0;
- while (passivechannels!=NULL && passivechannels[passivechans]!=NULL)
- {
- if(pt==NULL)
- {
- bytes_per_pixel+=2;
- }else{
- switch(pt[passivechans+activechans])
- {
- case HALF : bytes_per_pixel+=2;break;
- case FLOAT : case UINT : bytes_per_pixel+=4;break;
- default :
- cout << "wot?\n";
- exit(1);
- }
- }
- passivechans++;
- }
- int chans = activechans+passivechans;
-
- int bytes_per_bank = bytes_per_pixel/banks;
-
- int samples = (hdr.dataWindow().max.x+1-hdr.dataWindow().min.x)*
- (hdr.dataWindow().max.y+1-hdr.dataWindow().min.y)*chans;
- int size = samples*bytes_per_pixel;
-
- if(writing)
- {
- writingBuffer.resize(size);
- }else{
- readingBuffer.resize(size);
- }
-
- const char * write_ptr = writing ? &writingBuffer[0] : &readingBuffer[0];
- // fill with random halfs, casting to floats for float channels
- int chan=0;
- for (int i=0;i<samples;i++)
- {
- unsigned short int values = (unsigned short int) floor((double(rand())/double(RAND_MAX))*65535.0);
- half v;
- v.setBits(values);
- if (pt==NULL || pt[chan]==HALF)
- {
- *(half*)write_ptr = half(v);
- write_ptr+=2;
- } else {
- *(float*)write_ptr = float(v);
- write_ptr+=4;
- }
- chan++;
- if(chan==chans)
- {
- chan=0;
- }
-
- }
- if(!writing)
- {
- //take a copy of the buffer as it was before being read
- preReadBuffer = readingBuffer;
- }
-
- char* offset=NULL;
- ChannelList chanlist;
- int bytes_per_row = bytes_per_pixel*(dw.max.x+1-dw.min.x);
- int bytes_per_bank_row = bytes_per_row/banks;
-
- int first_pixel_index = bytes_per_row*dw.min.y+bytes_per_bank*dw.min.x;
-
- for (int i=0;i<chans;i++)
- {
- PixelType type = pt==NULL ? HALF : pt[i];
- if (i<activechans && writing)
- {
- chanlist.insert(channels[i],type);
- }
-
-
- if(i % (chans/banks) ==0)
- {
- //
- // set offset pointer to beginning of bank
- //
- int bank = i / (chans/banks);
- offset = (writing ? &writingBuffer[0] : &readingBuffer[0]) + bank*bytes_per_bank_row - first_pixel_index;
- }
-
- if(i<activechans)
- {
-
- buf.insert(channels[i],
- Slice(type,offset,bytes_per_bank,
- bytes_per_row,1,1,100+i));
- }else{
-
- if(!writing)
- {
-
- postreadbuf.insert(passivechannels[i-activechans],
- Slice(type,offset,bytes_per_bank,
- bytes_per_row,1,1,0.4));
-
- char * pre_offset = offset-&readingBuffer[0]+&preReadBuffer[0];
- prereadbuf.insert(passivechannels[i-activechans],
- Slice(type, pre_offset,bytes_per_bank,
- bytes_per_row,1,1,0.4));
- }
- }
- switch (type)
- {
- case HALF :
- offset+=2;
- break;
- case FLOAT :
- offset+=4;
- break;
- default :
- cout << "don't know about that\n";
- exit(1);
-
- }
- }
- return chanlist;
- }
- Box2i writefile(Schema & scheme,FrameBuffer& buf,bool tiny)
- {
- const int height = 128;
- const int width = 128;
- Header hdr(width,height,1);
-
-
- //min values in range (-100,100)
- hdr.dataWindow().min.x = int(200.0*double(rand())/double(RAND_MAX)-100.0);
- hdr.dataWindow().min.y = int(200.0*double(rand())/double(RAND_MAX)-100.0);
-
-
- // in tiny mode, make image up to 14*14 pixels (less than two SSE instructions)
- if(tiny)
- {
- hdr.dataWindow().max.x = hdr.dataWindow().min.x + 1+int(13*double(rand())/double(RAND_MAX));
- hdr.dataWindow().max.y = hdr.dataWindow().min.y + 1+int(13*double(rand())/double(RAND_MAX));
- }else{
- // in normal mode, make chunky images
- hdr.dataWindow().max.x = hdr.dataWindow().min.x + 64+int(400*double(rand())/double(RAND_MAX));
- hdr.dataWindow().max.y = hdr.dataWindow().min.y + 64+int(400*double(rand())/double(RAND_MAX));
-
- }
-
- hdr.compression()=ZIPS_COMPRESSION;
-
- FrameBuffer dummy1,dummy2;
-
- hdr.channels() = setupBuffer(hdr,scheme._active,scheme._passive,scheme._types,buf,dummy1,dummy2,scheme._banks,true);
-
- if(scheme._views)
- {
- addMultiView(hdr,scheme.views());
- }
-
- remove(filename);
- OutputFile f(filename,hdr);
- f.setFrameBuffer(buf);
- f.writePixels(hdr.dataWindow().max.y-hdr.dataWindow().min.y+1);
- return hdr.dataWindow();
- }
- bool readfile(Schema scheme,
- FrameBuffer & buf, ///< list of channels to read: index to readingBuffer
- FrameBuffer & preread,///< list of channels to skip: index to preReadBuffer
- FrameBuffer & postread)///< list of channels to skip: index to readingBuffer)
- {
- InputFile infile(filename);
- setupBuffer(infile.header(),scheme._active,scheme._passive, scheme._types,buf,preread,postread,scheme._banks,false);
- infile.setFrameBuffer(buf);
-
- cout.flush();
- infile.readPixels(infile.header().dataWindow().min.y,infile.header().dataWindow().max.y);
- return infile.isOptimizationEnabled();
- }
- void test(Schema writeScheme,Schema readScheme,bool nonfatal,bool tiny)
- {
- ostringstream q;
- q << writeScheme._name << " read as " << readScheme._name << "...";
- cout << left << setw(53) << q.str();
- FrameBuffer writeFrameBuf;
- Box2i dw = writefile(writeScheme,writeFrameBuf,tiny);
- FrameBuffer readFrameBuf;
- FrameBuffer preReadFrameBuf;
- FrameBuffer postReadFrameBuf;
- cout.flush();
- bool opt = readfile(readScheme,readFrameBuf,preReadFrameBuf,postReadFrameBuf);
- if(compare(readFrameBuf,writeFrameBuf,dw,nonfatal) &&
- compare(preReadFrameBuf,postReadFrameBuf,dw,nonfatal)
- )
- {
- cout << " OK ";
- if(opt)
- {
- cout << "OPTIMISED ";
- gOptimisedReads++;
- }
- cout << "\n";
- gSuccesses++;
- }else{
- cout << " FAIL" << endl;
- gFailures++;
- }
- remove(filename);
- }
- void runtests(bool nonfatal,bool tiny)
- {
- srand(1);
- int i=0;
- int skipped=0;
-
- gFailures=0;
- gSuccesses=0;
- gOptimisedReads=0;
-
-
- while(Schemes[i]._name!=NULL)
- {
- int j=0;
- while(Schemes[j]._name!=NULL)
- {
- cout << right << setw(2) << i << ',' << right << setw(2) << j << ": ";
- cout.flush();
- if(nonfatal)
- {
- cout << " skipping " << Schemes[i]._name << ',' << Schemes[j]._name << ": known to crash\n";
- skipped++;
- }
- else
- {
- test(Schemes[i],Schemes[j],nonfatal,tiny);
- }
- j++;
- }
- i++;
- }
- cout << gFailures << '/' << (gSuccesses+gFailures) << " runs failed\n";
- cout << skipped << " tests skipped (assumed to be bad)\n";
- cout << gOptimisedReads << '/' << gSuccesses << " optimised\n";
-
- if(gFailures>0 )
- {
- cout << " TESTS FAILED\n";
- assert(false);
- }
-
- }
- }
- void testOptimizedInterleavePatterns()
- {
- cout << "Testing SSE optimisation with different interleave patterns (large images) ... " << endl;
- runtests(false,false);
- cout << "Testing SSE optimisation with different interleave patterns (tiny images) ... " << endl;
- runtests(false,true);
- cout << "ok\n" << endl;
- }