<commit_before>#include "GaussianSmoothingFilter.hpp"
#include "Exception.hpp"
#include "DeviceManager.hpp"
#include "Image.hpp"
#include "DynamicImage.hpp"
using namespace fast;

void GaussianSmoothingFilter::setInput(ImageData::pointer input) {
    mInput = input;
    mIsModified = true;
    addParent(input);
    if(input->isDynamicData()) {
        mTempOutput = DynamicImage::New();
    } else {
        mTempOutput = Image::New();
        input->retain(mDevice);
    }
    mOutput = mTempOutput;
}

void GaussianSmoothingFilter::setDevice(ExecutionDevice::pointer device) {
    mDevice = device;
    mIsModified = true;
    mRecreateMask = true;
}

void GaussianSmoothingFilter::setMaskSize(unsigned char maskSize) {
    if(maskSize % 2 != 1)
        throw Exception("Mask size of GaussianSmoothingFilter must be odd.");

    mMaskSize = maskSize;
    mIsModified = true;
    mRecreateMask = true;
}

void GaussianSmoothingFilter::setStandardDeviation(float stdDev) {
    if(stdDev <= 0)
        throw Exception("Standard deviation of GaussianSmoothingFilter can't be less than 0.");

    mStdDev = stdDev;
    mIsModified = true;
    mRecreateMask = true;
}

ImageData::pointer GaussianSmoothingFilter::getOutput() {
    if(!mInput.isValid()) {
        throw Exception("Must call setInput before getOutput in GaussianSmoothingFilter");
    }
    if(mTempOutput.isValid()) {
        mTempOutput->setParent(mPtr.lock());

        ImageData::pointer newSmartPtr;
        newSmartPtr.swap(mTempOutput);

        return newSmartPtr;
    } else {
        return mOutput.lock();
    }
}

GaussianSmoothingFilter::GaussianSmoothingFilter() {
    mDevice = DeviceManager::getInstance().getDefaultComputationDevice();
    mStdDev = 1.0f;
    mMaskSize = 3;
    mIsModified = true;
    mRecreateMask = true;
    mDimensionCLCodeCompiledFor = 0;
}

GaussianSmoothingFilter::~GaussianSmoothingFilter() {
    delete[] mMask;
}

// TODO have to set mRecreateMask to true if input change dimension
void GaussianSmoothingFilter::createMask(Image::pointer input) {
    if(!mRecreateMask)
        return;

    unsigned char halfSize = (mMaskSize-1)/2;
    float sum = 0.0f;

    if(input->getDimensions() == 2) {
        mMask = new float[mMaskSize*mMaskSize];

        for(int x = -halfSize; x <= halfSize; x++) {
        for(int y = -halfSize; y <= halfSize; y++) {
            float value = exp(-(float)(x*x+y*y)/(2.0f*mStdDev*mStdDev));
            mMask[x+halfSize+(y+halfSize)*mMaskSize] = value;
            sum += value;
        }}

        for(int i = 0; i < mMaskSize*mMaskSize; i++)
            mMask[i] /= sum;
    } else if(input->getDimensions() == 3) {
         mMask = new float[mMaskSize*mMaskSize*mMaskSize];

        for(int x = -halfSize; x <= halfSize; x++) {
        for(int y = -halfSize; y <= halfSize; y++) {
        for(int z = -halfSize; z <= halfSize; z++) {
            float value = exp(-(float)(x*x+y*y+z*z)/(2.0f*mStdDev*mStdDev));
            mMask[x+halfSize+(y+halfSize)*mMaskSize+(z+halfSize)*mMaskSize*mMaskSize] = value;
            sum += value;
        }}}

        for(int i = 0; i < mMaskSize*mMaskSize*mMaskSize; i++)
            mMask[i] /= sum;
    }

    if(!mDevice->isHost()) {
        OpenCLDevice::pointer device = boost::static_pointer_cast<OpenCLDevice>(mDevice);
        unsigned int bufferSize = input->getDimensions() == 2 ? mMaskSize*mMaskSize : mMaskSize*mMaskSize*mMaskSize;
        mCLMask = cl::Buffer(
                device->getContext(),
                CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                sizeof(float)*bufferSize,
                mMask
        );
    }

    mRecreateMask = false;
}

void GaussianSmoothingFilter::recompileOpenCLCode(Image::pointer input) {
    // Check if there is a need to recompile OpenCL code
    if(input->getDimensions() == mDimensionCLCodeCompiledFor &&
            input->getDataType() == mTypeCLCodeCompiledFor)
        return;

    OpenCLDevice::pointer device = boost::static_pointer_cast<OpenCLDevice>(mDevice);
    std::string buildOptions = "";
    if(input->getDataType() == TYPE_FLOAT) {
        buildOptions = "-DTYPE_FLOAT";
    } else if(input->getDataType() == TYPE_INT8 || input->getDataType() == TYPE_INT16) {
        buildOptions = "-DTYPE_INT";
    } else {
        buildOptions = "-DTYPE_UINT";
    }
    std::string filename;
    if(input->getDimensions() == 2) {
        filename = "Algorithms/GaussianSmoothingFilter2D.cl";
    } else {
        filename = "Algorithms/GaussianSmoothingFilter3D.cl";
    }
    int programNr = device->createProgramFromSource(std::string(FAST_ROOT_DIR) + filename, buildOptions);
    mKernel = cl::Kernel(device->getProgram(programNr), "gaussianSmoothing");
    mDimensionCLCodeCompiledFor = input->getDimensions();
    mTypeCLCodeCompiledFor = input->getDataType();
}

template <class T>
void executeAlgorithmOnHost(Image::pointer input, Image::pointer output, float * mask, unsigned char maskSize) {
    unsigned int nrOfComponents = input->getNrOfComponents();
    if(nrOfComponents != 1)
        throw Exception("Running the gaussian smoothing filter on an image with more than 1 component on the host is currently not supported.");
    ImageAccess inputAccess = input->getImageAccess(ACCESS_READ);
    ImageAccess outputAccess = output->getImageAccess(ACCESS_READ_WRITE);

    T * inputData = (T*)inputAccess.get();
    T * outputData = (T*)outputAccess.get();

    const unsigned char halfSize = (maskSize-1)/2;
    unsigned int width = input->getWidth();
    unsigned int height = input->getHeight();
    if(input->getDimensions() == 3) {
        unsigned int depth = input->getDepth();
        for(unsigned int z = halfSize; z < depth-halfSize; z++) {
        for(unsigned int y = halfSize; y < height-halfSize; y++) {
        for(unsigned int x = halfSize; x < width-halfSize; x++) {

            double sum = 0.0;
            for(int c = -halfSize; c <= halfSize; c++) {
            for(int b = -halfSize; b <= halfSize; b++) {
            for(int a = -halfSize; a <= halfSize; a++) {
                sum += mask[a+halfSize+(b+halfSize)*maskSize+(c+halfSize)*maskSize*maskSize]*
                        inputData[x+a+(y+b)*width+(z+c)*width*height];
            }}}
            outputData[x+y*width+z*width*height] = (T)sum;
        }}}
    } else {
        for(unsigned int y = halfSize; y < height-halfSize; y++) {
        for(unsigned int x = halfSize; x < width-halfSize; x++) {

            double sum = 0.0;
            for(int b = -halfSize; b <= halfSize; b++) {
            for(int a = -halfSize; a <= halfSize; a++) {
                sum += mask[a+halfSize+(b+halfSize)*maskSize]*
                        inputData[x+a+(y+b)*width];
            }}
            outputData[x+y*width] = (T)sum;
        }}
    }
}

void GaussianSmoothingFilter::execute() {

    Image::pointer input;
    if(!mInput.isValid()) {
        throw Exception("No input supplied to GaussianSmoothingFilter");
    }
    if(!mOutput.lock().isValid()) {
        // output object is no longer valid
        return;
    }
    if(mInput->isDynamicData()) {
        input = DynamicImage::pointer(mInput)->getNextFrame();
    } else {
        input = mInput;
    }

    Image::pointer output;
    if(mInput->isDynamicData()) {
        output = Image::New();
        DynamicImage::pointer(mOutput)->addFrame(output);
    } else {
        output = Image::pointer(mOutput);
    }

    // Initialize output image
    if(input->getDimensions() == 2) {
        output->create2DImage(input->getWidth(),
            input->getHeight(),
            input->getDataType(),
            input->getNrOfComponents(),
            mDevice);
    } else {
         output->create3DImage(input->getWidth(),
            input->getHeight(),
            input->getDepth(),
            input->getDataType(),
            input->getNrOfComponents(),
            mDevice);
    }

    createMask(input);

    if(mDevice->isHost()) {
        switch(input->getDataType()) {
            fastSwitchTypeMacro(executeAlgorithmOnHost<FAST_TYPE>(input, output, mMask, mMaskSize));
        }
    } else {
        OpenCLDevice::pointer device = boost::static_pointer_cast<OpenCLDevice>(mDevice);

        recompileOpenCLCode(input);
        cl::NDRange globalSize;
        if(input->getDimensions() == 2) {
            globalSize = cl::NDRange(input->getWidth(),input->getHeight());

            OpenCLImageAccess2D inputAccess = input->getOpenCLImageAccess2D(ACCESS_READ, device);
            OpenCLImageAccess2D outputAccess = output->getOpenCLImageAccess2D(ACCESS_READ_WRITE, device);
            mKernel.setArg(0, *inputAccess.get());
            mKernel.setArg(2, *outputAccess.get());
        } else {
            globalSize = cl::NDRange(input->getWidth(),input->getHeight(),input->getDepth());

            OpenCLImageAccess3D inputAccess = input->getOpenCLImageAccess3D(ACCESS_READ, device);
            OpenCLImageAccess3D outputAccess = output->getOpenCLImageAccess3D(ACCESS_READ_WRITE, device);
            mKernel.setArg(0, *inputAccess.get());
            mKernel.setArg(2, *outputAccess.get());
        }

        mKernel.setArg(1, mCLMask);
        mKernel.setArg(3, mMaskSize);

        device->getCommandQueue().enqueueNDRangeKernel(
                mKernel,
                cl::NullRange,
                globalSize,
                cl::NullRange
        );
    }

    if(!mInput->isDynamicData())
        mInput->release(mDevice);

    // Update the timestamp of the output data
    output->updateModifiedTimestamp();
}

void GaussianSmoothingFilter::waitToFinish() {
    if(!mDevice->isHost()) {
        OpenCLDevice::pointer device = boost::static_pointer_cast<OpenCLDevice>(mDevice);
        device->getCommandQueue().finish();
    }
}
<commit_msg>the host code of the gaussian smoothing filter now supports images with multiple components, however it will currently only process the first component<commit_after>#include "GaussianSmoothingFilter.hpp"
#include "Exception.hpp"
#include "DeviceManager.hpp"
#include "Image.hpp"
#include "DynamicImage.hpp"
using namespace fast;

void GaussianSmoothingFilter::setInput(ImageData::pointer input) {
    mInput = input;
    mIsModified = true;
    addParent(input);
    if(input->isDynamicData()) {
        mTempOutput = DynamicImage::New();
    } else {
        mTempOutput = Image::New();
        input->retain(mDevice);
    }
    mOutput = mTempOutput;
}

void GaussianSmoothingFilter::setDevice(ExecutionDevice::pointer device) {
    mDevice = device;
    mIsModified = true;
    mRecreateMask = true;
}

void GaussianSmoothingFilter::setMaskSize(unsigned char maskSize) {
    if(maskSize % 2 != 1)
        throw Exception("Mask size of GaussianSmoothingFilter must be odd.");

    mMaskSize = maskSize;
    mIsModified = true;
    mRecreateMask = true;
}

void GaussianSmoothingFilter::setStandardDeviation(float stdDev) {
    if(stdDev <= 0)
        throw Exception("Standard deviation of GaussianSmoothingFilter can't be less than 0.");

    mStdDev = stdDev;
    mIsModified = true;
    mRecreateMask = true;
}

ImageData::pointer GaussianSmoothingFilter::getOutput() {
    if(!mInput.isValid()) {
        throw Exception("Must call setInput before getOutput in GaussianSmoothingFilter");
    }
    if(mTempOutput.isValid()) {
        mTempOutput->setParent(mPtr.lock());

        ImageData::pointer newSmartPtr;
        newSmartPtr.swap(mTempOutput);

        return newSmartPtr;
    } else {
        return mOutput.lock();
    }
}

GaussianSmoothingFilter::GaussianSmoothingFilter() {
    mDevice = DeviceManager::getInstance().getDefaultComputationDevice();
    mStdDev = 1.0f;
    mMaskSize = 3;
    mIsModified = true;
    mRecreateMask = true;
    mDimensionCLCodeCompiledFor = 0;
}

GaussianSmoothingFilter::~GaussianSmoothingFilter() {
    delete[] mMask;
}

// TODO have to set mRecreateMask to true if input change dimension
void GaussianSmoothingFilter::createMask(Image::pointer input) {
    if(!mRecreateMask)
        return;

    unsigned char halfSize = (mMaskSize-1)/2;
    float sum = 0.0f;

    if(input->getDimensions() == 2) {
        mMask = new float[mMaskSize*mMaskSize];

        for(int x = -halfSize; x <= halfSize; x++) {
        for(int y = -halfSize; y <= halfSize; y++) {
            float value = exp(-(float)(x*x+y*y)/(2.0f*mStdDev*mStdDev));
            mMask[x+halfSize+(y+halfSize)*mMaskSize] = value;
            sum += value;
        }}

        for(int i = 0; i < mMaskSize*mMaskSize; i++)
            mMask[i] /= sum;
    } else if(input->getDimensions() == 3) {
         mMask = new float[mMaskSize*mMaskSize*mMaskSize];

        for(int x = -halfSize; x <= halfSize; x++) {
        for(int y = -halfSize; y <= halfSize; y++) {
        for(int z = -halfSize; z <= halfSize; z++) {
            float value = exp(-(float)(x*x+y*y+z*z)/(2.0f*mStdDev*mStdDev));
            mMask[x+halfSize+(y+halfSize)*mMaskSize+(z+halfSize)*mMaskSize*mMaskSize] = value;
            sum += value;
        }}}

        for(int i = 0; i < mMaskSize*mMaskSize*mMaskSize; i++)
            mMask[i] /= sum;
    }

    if(!mDevice->isHost()) {
        OpenCLDevice::pointer device = boost::static_pointer_cast<OpenCLDevice>(mDevice);
        unsigned int bufferSize = input->getDimensions() == 2 ? mMaskSize*mMaskSize : mMaskSize*mMaskSize*mMaskSize;
        mCLMask = cl::Buffer(
                device->getContext(),
                CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                sizeof(float)*bufferSize,
                mMask
        );
    }

    mRecreateMask = false;
}

void GaussianSmoothingFilter::recompileOpenCLCode(Image::pointer input) {
    // Check if there is a need to recompile OpenCL code
    if(input->getDimensions() == mDimensionCLCodeCompiledFor &&
            input->getDataType() == mTypeCLCodeCompiledFor)
        return;

    OpenCLDevice::pointer device = boost::static_pointer_cast<OpenCLDevice>(mDevice);
    std::string buildOptions = "";
    if(input->getDataType() == TYPE_FLOAT) {
        buildOptions = "-DTYPE_FLOAT";
    } else if(input->getDataType() == TYPE_INT8 || input->getDataType() == TYPE_INT16) {
        buildOptions = "-DTYPE_INT";
    } else {
        buildOptions = "-DTYPE_UINT";
    }
    std::string filename;
    if(input->getDimensions() == 2) {
        filename = "Algorithms/GaussianSmoothingFilter2D.cl";
    } else {
        filename = "Algorithms/GaussianSmoothingFilter3D.cl";
    }
    int programNr = device->createProgramFromSource(std::string(FAST_ROOT_DIR) + filename, buildOptions);
    mKernel = cl::Kernel(device->getProgram(programNr), "gaussianSmoothing");
    mDimensionCLCodeCompiledFor = input->getDimensions();
    mTypeCLCodeCompiledFor = input->getDataType();
}

template <class T>
void executeAlgorithmOnHost(Image::pointer input, Image::pointer output, float * mask, unsigned char maskSize) {
    // TODO: this method currently only processes the first component
    unsigned int nrOfComponents = input->getNrOfComponents();
    ImageAccess inputAccess = input->getImageAccess(ACCESS_READ);
    ImageAccess outputAccess = output->getImageAccess(ACCESS_READ_WRITE);

    T * inputData = (T*)inputAccess.get();
    T * outputData = (T*)outputAccess.get();

    const unsigned char halfSize = (maskSize-1)/2;
    unsigned int width = input->getWidth();
    unsigned int height = input->getHeight();
    if(input->getDimensions() == 3) {
        unsigned int depth = input->getDepth();
        for(unsigned int z = halfSize; z < depth-halfSize; z++) {
        for(unsigned int y = halfSize; y < height-halfSize; y++) {
        for(unsigned int x = halfSize; x < width-halfSize; x++) {

            double sum = 0.0;
            for(int c = -halfSize; c <= halfSize; c++) {
            for(int b = -halfSize; b <= halfSize; b++) {
            for(int a = -halfSize; a <= halfSize; a++) {
                sum += mask[a+halfSize+(b+halfSize)*maskSize+(c+halfSize)*maskSize*maskSize]*
                        inputData[(x+a)*nrOfComponents+(y+b)*nrOfComponents*width+(z+c)*nrOfComponents*width*height];
            }}}
            outputData[x*nrOfComponents+y*nrOfComponents*width+z*nrOfComponents*width*height] = (T)sum;
        }}}
    } else {
        for(unsigned int y = halfSize; y < height-halfSize; y++) {
        for(unsigned int x = halfSize; x < width-halfSize; x++) {

            double sum = 0.0;
            for(int b = -halfSize; b <= halfSize; b++) {
            for(int a = -halfSize; a <= halfSize; a++) {
                sum += mask[a+halfSize+(b+halfSize)*maskSize]*
                        inputData[(x+a)*nrOfComponents+(y+b)*nrOfComponents*width];
            }}
            outputData[x*nrOfComponents+y*nrOfComponents*width] = (T)sum;
        }}
    }
}

void GaussianSmoothingFilter::execute() {

    Image::pointer input;
    if(!mInput.isValid()) {
        throw Exception("No input supplied to GaussianSmoothingFilter");
    }
    if(!mOutput.lock().isValid()) {
        // output object is no longer valid
        return;
    }
    if(mInput->isDynamicData()) {
        input = DynamicImage::pointer(mInput)->getNextFrame();
    } else {
        input = mInput;
    }

    Image::pointer output;
    if(mInput->isDynamicData()) {
        output = Image::New();
        DynamicImage::pointer(mOutput)->addFrame(output);
    } else {
        output = Image::pointer(mOutput);
    }

    // Initialize output image
    if(input->getDimensions() == 2) {
        output->create2DImage(input->getWidth(),
            input->getHeight(),
            input->getDataType(),
            input->getNrOfComponents(),
            mDevice);
    } else {
         output->create3DImage(input->getWidth(),
            input->getHeight(),
            input->getDepth(),
            input->getDataType(),
            input->getNrOfComponents(),
            mDevice);
    }

    createMask(input);

    if(mDevice->isHost()) {
        switch(input->getDataType()) {
            fastSwitchTypeMacro(executeAlgorithmOnHost<FAST_TYPE>(input, output, mMask, mMaskSize));
        }
    } else {
        OpenCLDevice::pointer device = boost::static_pointer_cast<OpenCLDevice>(mDevice);

        recompileOpenCLCode(input);
        cl::NDRange globalSize;
        if(input->getDimensions() == 2) {
            globalSize = cl::NDRange(input->getWidth(),input->getHeight());

            OpenCLImageAccess2D inputAccess = input->getOpenCLImageAccess2D(ACCESS_READ, device);
            OpenCLImageAccess2D outputAccess = output->getOpenCLImageAccess2D(ACCESS_READ_WRITE, device);
            mKernel.setArg(0, *inputAccess.get());
            mKernel.setArg(2, *outputAccess.get());
        } else {
            globalSize = cl::NDRange(input->getWidth(),input->getHeight(),input->getDepth());

            OpenCLImageAccess3D inputAccess = input->getOpenCLImageAccess3D(ACCESS_READ, device);
            OpenCLImageAccess3D outputAccess = output->getOpenCLImageAccess3D(ACCESS_READ_WRITE, device);
            mKernel.setArg(0, *inputAccess.get());
            mKernel.setArg(2, *outputAccess.get());
        }

        mKernel.setArg(1, mCLMask);
        mKernel.setArg(3, mMaskSize);

        device->getCommandQueue().enqueueNDRangeKernel(
                mKernel,
                cl::NullRange,
                globalSize,
                cl::NullRange
        );
    }

    if(!mInput->isDynamicData())
        mInput->release(mDevice);

    // Update the timestamp of the output data
    output->updateModifiedTimestamp();
}

void GaussianSmoothingFilter::waitToFinish() {
    if(!mDevice->isHost()) {
        OpenCLDevice::pointer device = boost::static_pointer_cast<OpenCLDevice>(mDevice);
        device->getCommandQueue().finish();
    }
}
<|endoftext|>


<commit_before>/** Copyright (C) 2019 European Spallation Source ERIC */

#include <gdgem/generators/BuilderReadouts.h>
#include <gdgem/srs/SRSMappings.h>
#include <test/TestBase.h>

std::vector<uint8_t> OneReadout {
  0x00, // fec 0
  0x00, // chip 0
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // time 2 LE
  0x01, 0x00, // channel 1
  0xbb, 0xaa, // bcid 0xaabb
  0x01, 0x00, // tdc 0x01
  0x23, 0x01, // adc 0x0123
  0x00,       // over threshold (bool?) false
  0x00, 0x00, 0x00, 0x00, // chiptime (float?) 0.0
};

std::vector<uint8_t> OneReadoutAdcUnderThreshold {
  0x00, // fec 0
  0x00, // chip 0
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // time 2 LE
  0x01, 0x00, // channel 1
  0xbb, 0xaa, // bcid 0xaabb
  0x01, 0x00, // tdc 0x01
  0x01, 0x00, // adc 0x0001
  0x00,       // over threshold (bool?) false
  0x00, 0x00, 0x00, 0x00, // chiptime (float?) 0.0
};

using namespace Gem;

class BuilderReadoutsTest : public TestBase {
protected:
  SRSMappings Mappings;
  const uint16_t AdcThreshold{128};
  const std::string DumpDirectory{"./"};

  void SetUp() override {
    // Define minimal mappings (copied from SRSMappingstest)
    // Fec 0, VMM 0, Plane 0, channel offset 0
    Mappings.set_mapping(0, 0, 0, 0);
  }
  void TearDown() override {}
};

// Most data in BuilderReadouts is private
// Check counters and data structures inherited from
// AbstractBuilder
TEST_F(BuilderReadoutsTest, Constructor) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  ASSERT_EQ(BR.stats.parser_readouts, 0);
}

TEST_F(BuilderReadoutsTest, ParseOneReadout) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  BR.process_buffer((char*)&OneReadout[0], OneReadout.size());
  ASSERT_EQ(BR.stats.geom_errors, 0);
  ASSERT_EQ(BR.hit_buffer_x.size(), 1);
  ASSERT_EQ(BR.hit_buffer_y.size(), 0);
}

TEST_F(BuilderReadoutsTest, AdcUnderThreshold) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  BR.process_buffer((char*)&OneReadoutAdcUnderThreshold[0],
        OneReadoutAdcUnderThreshold.size());
  ASSERT_EQ(BR.stats.geom_errors, 0);
  ASSERT_EQ(BR.stats.adc_rejects, 1);
  ASSERT_EQ(BR.hit_buffer_x.size(), 0);
  ASSERT_EQ(BR.hit_buffer_y.size(), 0);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>more test cases<commit_after>/** Copyright (C) 2019 European Spallation Source ERIC */

#include <gdgem/generators/BuilderReadouts.h>
#include <gdgem/srs/SRSMappings.h>
#include <test/TestBase.h>

std::vector<uint8_t> OneReadout {
  0x00, // fec 0
  0x00, // chip 0
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // time 2 LE
  0x01, 0x00, // channel 1
  0xbb, 0xaa, // bcid 0xaabb
  0x01, 0x00, // tdc 0x01
  0x23, 0x01, // adc 0x0123
  0x00,       // over threshold (bool?) false
  0x00, 0x00, 0x00, 0x00, // chiptime (float?) 0.0
};

std::vector<uint8_t> OneReadoutY {
  0x01, // fec 0
  0x00, // chip 0
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // time 2 LE
  0x01, 0x00, // channel 1
  0xbb, 0xaa, // bcid 0xaabb
  0x01, 0x00, // tdc 0x01
  0x23, 0x01, // adc 0x0123
  0x00,       // over threshold (bool?) false
  0x00, 0x00, 0x00, 0x00, // chiptime (float?) 0.0
};

std::vector<uint8_t> OneReadoutBadCoord {
  0x00, // fec 0
  0x00, // chip 0
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // time 2 LE
  0xff, 0xff, // channel 0xffff
  0xbb, 0xaa, // bcid 0xaabb
  0x01, 0x00, // tdc 0x01
  0x23, 0x01, // adc 0x0123
  0x00,       // over threshold (bool?) false
  0x00, 0x00, 0x00, 0x00, // chiptime (float?) 0.0
};

std::vector<uint8_t> OneReadoutAdcUnderThreshold {
  0x00, // fec 0
  0x00, // chip 0
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // time 2 LE
  0x01, 0x00, // channel 1
  0xbb, 0xaa, // bcid 0xaabb
  0x01, 0x00, // tdc 0x01
  0x01, 0x00, // adc 0x0001
  0x00,       // over threshold (bool?) false
  0x00, 0x00, 0x00, 0x00, // chiptime (float?) 0.0
};

std::vector<uint8_t> OneReadoutBadPlane {
  0x10, // fec 0
  0x10, // chip 0
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // time 2 LE
  0x01, 0x00, // channel 1
  0xbb, 0xaa, // bcid 0xaabb
  0x01, 0x00, // tdc 0x01
  0x23, 0x01, // adc 0x0123
  0x00,       // over threshold (bool?) false
  0x00, 0x00, 0x00, 0x00, // chiptime (float?) 0.0
};

using namespace Gem;

class BuilderReadoutsTest : public TestBase {
protected:
  SRSMappings Mappings;
  const uint16_t AdcThreshold{128};
  const std::string DumpDirectory{"./"};

  void SetUp() override {
    // Define minimal mappings (copied from SRSMappingstest)
    // Fec 0, VMM 0, Plane 0, channel offset 0
    Mappings.set_mapping(0, 0, 0, 0);

    // Fec 0, VMM 0, Plane 0, channel offset 0
    Mappings.set_mapping(1, 0, 1, 0);
  }
  void TearDown() override {}
};

// Most data in BuilderReadouts is private
// Check counters and data structures inherited from
// AbstractBuilder
TEST_F(BuilderReadoutsTest, Constructor) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  ASSERT_EQ(BR.stats.parser_readouts, 0);
  ASSERT_EQ(BR.stats.geom_errors, 0);
  ASSERT_EQ(BR.hit_buffer_x.size(), 0);
  ASSERT_EQ(BR.hit_buffer_y.size(), 0);
}

TEST_F(BuilderReadoutsTest, BadPlane) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  BR.process_buffer((char*)&OneReadoutBadPlane[0], OneReadoutBadPlane.size());
  ASSERT_EQ(BR.stats.geom_errors, 1);
}

TEST_F(BuilderReadoutsTest, BadCoord) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  BR.process_buffer((char*)&OneReadoutBadCoord[0], OneReadoutBadCoord.size());
  ASSERT_EQ(BR.stats.geom_errors, 1);
}

TEST_F(BuilderReadoutsTest, ParseOneReadoutX) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  BR.process_buffer((char*)&OneReadout[0], OneReadout.size());
  ASSERT_EQ(BR.stats.geom_errors, 0);
  ASSERT_EQ(BR.hit_buffer_x.size(), 1);
  ASSERT_EQ(BR.hit_buffer_y.size(), 0);
}

TEST_F(BuilderReadoutsTest, ParseOneReadoutY) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  BR.process_buffer((char*)&OneReadoutY[0], OneReadoutY.size());
  ASSERT_EQ(BR.stats.geom_errors, 0);
  ASSERT_EQ(BR.hit_buffer_x.size(), 0);
  ASSERT_EQ(BR.hit_buffer_y.size(), 1);
}

TEST_F(BuilderReadoutsTest, AdcUnderThreshold) {
  BuilderReadouts BR(Mappings, AdcThreshold, DumpDirectory);
  BR.process_buffer((char*)&OneReadoutAdcUnderThreshold[0],
        OneReadoutAdcUnderThreshold.size());
  ASSERT_EQ(BR.stats.geom_errors, 0);
  ASSERT_EQ(BR.stats.adc_rejects, 1);
  ASSERT_EQ(BR.hit_buffer_x.size(), 0);
  ASSERT_EQ(BR.hit_buffer_y.size(), 0);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <memory>

#include "HexagonOffload.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Closure.h"
#include "Param.h"
#include "Image.h"
#include "LLVM_Output.h"
#include "RemoveTrivialForLoops.h"
#include "InjectHostDevBufferCopies.h"
#include "LLVM_Headers.h"

namespace Halide {
namespace Internal {

using std::string;
using std::vector;

namespace {

// Replace the parameter objects of loads/stores with a new parameter
// object.
class ReplaceParams : public IRMutator {
    const std::map<std::string, Parameter> &replacements;

    using IRMutator::visit;

    void visit(const Load *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            expr = Load::make(op->type, op->name, mutate(op->index), op->image, i->second);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Store *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            stmt = Store::make(op->name, mutate(op->value), mutate(op->index), i->second);
        } else {
            IRMutator::visit(op);
        }
    }

public:
    ReplaceParams(const std::map<std::string, Parameter> &replacements)
        : replacements(replacements) {}
};

Stmt replace_params(Stmt s, const std::map<std::string, Parameter> &replacements) {
    return ReplaceParams(replacements).mutate(s);
}

class InjectHexagonRpc : public IRMutator {
    std::map<std::string, Expr> state_vars;

    Module device_code;

    /** Alignment info for Int(32) variables in scope, so we don't
     * lose the information when creating Hexagon kernels. */
    Scope<ModulusRemainder> alignment_info;

    Expr state_var(const std::string& name, Type type) {
        Expr& var = state_vars[name];
        if (!var.defined()) {
            Buffer storage(type, {}, nullptr, name + "_buf");
            *(void **)storage.host_ptr() = nullptr;
            var = Load::make(type_of<void*>(), name + "_buf", 0, storage, Parameter());
        }
        return var;
    }

    Expr state_var_ptr(const std::string& name, Type type) {
        Expr var = state_var(name, type);
        return Call::make(Handle(), Call::address_of, {var}, Call::Intrinsic);
    }

    Expr module_state() {
        return state_var("hexagon_module_state", type_of<void*>());
    }

    Expr module_state_ptr() {
        return state_var_ptr("hexagon_module_state", type_of<void*>());
    }

    // Create a Buffer containing the given buffer/size, and return an
    // expression for a pointer to the first element.
    Expr buffer_ptr(const uint8_t* buffer, size_t size, const char* name) {
        Buffer code(type_of<uint8_t>(), {(int)size}, nullptr, name);
        memcpy(code.host_ptr(), buffer, (int)size);

        Expr ptr_0 = Load::make(type_of<uint8_t>(), name, 0, code, Parameter());
        return Call::make(Handle(), Call::address_of, {ptr_0}, Call::Intrinsic);
    }

    using IRMutator::visit;

    void visit(const For *loop) {
        if (loop->device_api == DeviceAPI::Hexagon) {
            // Unrolling or loop partitioning might generate multiple
            // loops with the same name, so we need to make them unique.
            std::string hex_name = unique_name("hex_" + loop->name);

            // After moving this to Hexagon, it doesn't need to be
            // marked Hexagon anymore.
            Stmt body = For::make(loop->name, loop->min, loop->extent, loop->for_type,
                                  DeviceAPI::None, loop->body);
            body = remove_trivial_for_loops(body);

            // Build a closure for the device code.
            // TODO: Should this move the body of the loop to Hexagon,
            // or the loop itself? Currently, this moves the loop itself.
            Closure c(body);

            // Make an argument list, and generate a function in the
            // device_code module. The hexagon runtime code expects
            // the arguments to appear in the order of (input buffers,
            // output buffers, input scalars).  Scalars must be last
            // for the scalar arguments to shadow the symbols of the
            // buffer that get generated by CodeGen_LLVM.
            std::vector<LoweredArgument> input_buffers, output_buffers;
            std::map<std::string, Parameter> replacement_params;
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    Argument::Kind kind = Argument::OutputBuffer;
                    output_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                } else {
                    Argument::Kind kind = Argument::InputBuffer;
                    input_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                }

                // Build a parameter to replace.
                Parameter p(i.second.type, true, i.second.dimensions);
                // Assert that buffers are aligned to one HVX vector.
                const int alignment = 128;
                p.set_host_alignment(alignment);
                // The other parameter constraints are already
                // accounted for by the closure grabbing those
                // arguments, so we only need to provide the host
                // alignment.
                replacement_params[i.first] = p;

                // Add an assert to the body that validates the
                // alignment of the buffer.
                if (!device_code.target().has_feature(Target::NoAsserts)) {
                    Expr host_ptr = reinterpret<uint64_t>(Variable::make(Handle(), i.first + ".host"));
                    Expr error = Call::make(Int(32), "halide_error_unaligned_host_ptr",
                                            {i.first, alignment}, Call::Extern);
                    body = Block::make(AssertStmt::make(host_ptr % alignment == 0, error), body);
                }
            }
            body = replace_params(body, replacement_params);

            std::vector<LoweredArgument> args;
            args.insert(args.end(), input_buffers.begin(), input_buffers.end());
            args.insert(args.end(), output_buffers.begin(), output_buffers.end());
            for (const auto& i : c.vars) {
                LoweredArgument arg(i.first, Argument::InputScalar, i.second, 0);
                if (alignment_info.contains(i.first)) {
                    arg.alignment = alignment_info.get(i.first);
                }
                args.push_back(arg);
            }
            device_code.append(LoweredFunc(hex_name, args, body, LoweredFunc::External));

            // Generate a call to hexagon_device_run.
            std::vector<Expr> arg_sizes;
            std::vector<Expr> arg_ptrs;
            std::vector<Expr> arg_flags;

            for (const auto& i : c.buffers) {
                arg_sizes.push_back(Expr((uint64_t) sizeof(buffer_t*)));
                arg_ptrs.push_back(Variable::make(type_of<buffer_t *>(), i.first + ".buffer"));
                // In the flags parameter, bit 0 set indicates the
                // buffer is read, bit 1 set indicates the buffer is
                // written. If neither are set, the argument is a scalar.
                int flags = 0;
                if (i.second.read) flags |= 0x1;
                if (i.second.write) flags |= 0x2;
                arg_flags.push_back(flags);
            }
            for (const auto& i : c.vars) {
                Expr arg = Variable::make(i.second, i.first);
                Expr arg_ptr = Call::make(type_of<void *>(), Call::make_struct, {arg}, Call::Intrinsic);

                // sizeof(scalar-type) will always fit into int32
                arg_sizes.push_back(Expr((uint64_t) i.second.bytes()));
                arg_ptrs.push_back(arg_ptr);
                arg_flags.push_back(0x0);
            }

            // The argument list is terminated with an argument of size 0.
            arg_sizes.push_back(Expr((uint64_t) 0));

            std::string pipeline_name = hex_name + "_argv";
            std::vector<Expr> params;
            params.push_back(module_state());
            params.push_back(pipeline_name);
            params.push_back(state_var_ptr(hex_name, type_of<int>()));
            params.push_back(Call::make(type_of<size_t*>(), Call::make_struct, arg_sizes, Call::Intrinsic));
            params.push_back(Call::make(type_of<void**>(), Call::make_struct, arg_ptrs, Call::Intrinsic));
            params.push_back(Call::make(type_of<int*>(), Call::make_struct, arg_flags, Call::Intrinsic));

            stmt = call_extern_and_assert("halide_hexagon_run", params);

        } else {
            IRMutator::visit(loop);
        }
    }

    void visit(const Let *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

    void visit(const LetStmt *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

public:
    InjectHexagonRpc(const Target &target) : device_code("hexagon", target) {}

    Stmt inject(Stmt s) {
        s = mutate(s);

        // Skip if there are no device kernels.
        if (device_code.functions().empty()) {
            return s;
        }

        // Compile the device code.
        // TODO: Currently, this requires shelling out to
        // hexagon-clang from the Qualcomm Hexagon SDK, because the
        // Hexagon LLVM target is not fully open source yet. When the
        // LLVM Hexagon target is fully open sourced, we can instead
        // just compile the module to an object, and find a way to
        // link it to a shared object.
        debug(1) << "Hexagon device code module: " << device_code << "\n";

        // First compile the module to an llvm module
        llvm::LLVMContext context;
        std::unique_ptr<llvm::Module> llvm_module =
            compile_module_to_llvm_module(device_code, context);

        #if LLVM_VERSION >= 39
        // Then mess with it, to fix up version differences between
        // our LLVM and hexagon-clang. Yuck.
        for (auto &gv : llvm_module->globals()) {
            // hexagon-clang doesn't understand the local_unnamed_addr
            // attribute, so we must strip it.
            gv.setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::None);
        }
        for (auto &fn : llvm_module->functions()) {
            fn.setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::None);
        }
        #endif

        // Dump the llvm module to a temp file as .ll
        TemporaryFile tmp_bitcode("hex", ".ll");
        TemporaryFile tmp_shared_object("hex", ".o");
        std::unique_ptr<llvm::raw_fd_ostream> ostream =
            make_raw_fd_ostream(tmp_bitcode.pathname());
        compile_llvm_module_to_llvm_assembly(*llvm_module, *ostream);
        ostream->flush();

        // Shell out to hexagon clang to compile it.
        string hex_command;

        const char *path = getenv("HL_HEXAGON_CLANG");
        if (path && path[0]) {
            hex_command = path;
        } else {
            path = getenv("HL_HEXAGON_TOOLS");
            if (path && path[0]) {
                hex_command = string(path) + "/bin/hexagon-clang";
            } else {
                user_error << "Unable to find hexagon-clang: neither HL_HEXAGON_CLANG nor HL_HEXAGON_TOOLS are set properly.";
            }
        }

        hex_command += " -c ";
        hex_command += tmp_bitcode.pathname();
        hex_command += " -fno-pic -O3 -mllvm -lsr-complexity-limit=65535 -Wno-override-module ";
        if (device_code.target().has_feature(Target::HVX_v62)) {
            hex_command += " -mv62";
        }
        if (device_code.target().has_feature(Target::HVX_128)) {
            hex_command += " -mhvx-double";
        }
        hex_command += " -o " + tmp_shared_object.pathname();
        int result = system(hex_command.c_str());
        internal_assert(result == 0) << "hexagon-clang failed\n";

        // Read the compiled object back in and put it in a buffer in the module
        std::ifstream so(tmp_shared_object.pathname(), std::ios::binary | std::ios::ate);
        internal_assert(so.good()) << "failed to open temporary shared object.";
        std::vector<uint8_t> object(so.tellg());
        so.seekg(0, std::ios::beg);
        so.read(reinterpret_cast<char*>(&object[0]), object.size());

        size_t code_size = object.size();
        Expr code_ptr = buffer_ptr(&object[0], code_size, "hexagon_code");

        // Wrap the statement in calls to halide_initialize_kernels.
        Stmt init_kernels = call_extern_and_assert("halide_hexagon_initialize_kernels",
                                                   {module_state_ptr(), code_ptr, Expr((uint64_t) code_size)});
        s = Block::make(init_kernels, s);

        return s;
    }
};

}

Stmt inject_hexagon_rpc(Stmt s, const Target &host_target) {
    // Make a new target for the device module.
    Target target(Target::NoOS, Target::Hexagon, 32);

    // These feature flags are propagated from the host target to the
    // device module.
    //
    // TODO: We'd like Target::Debug to be in this list too, but trunk
    // llvm currently disagrees with hexagon clang as to what
    // constitutes valid debug info.
    static const Target::Feature shared_features[] = {
        Target::NoAsserts,
        Target::HVX_64,
        Target::HVX_128,
        Target::HVX_v62
    };
    for (Target::Feature i : shared_features) {
        if (host_target.has_feature(i)) {
            target = target.with_feature(i);
        }
    }

    InjectHexagonRpc injector(target);
    s = injector.inject(s);
    return s;
}

}  // namespace Internal
}  // namespace Halide
<commit_msg>Add -mlong-calls<commit_after>#include <iostream>
#include <fstream>
#include <memory>

#include "HexagonOffload.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Closure.h"
#include "Param.h"
#include "Image.h"
#include "LLVM_Output.h"
#include "RemoveTrivialForLoops.h"
#include "InjectHostDevBufferCopies.h"
#include "LLVM_Headers.h"

namespace Halide {
namespace Internal {

using std::string;
using std::vector;

namespace {

// Replace the parameter objects of loads/stores with a new parameter
// object.
class ReplaceParams : public IRMutator {
    const std::map<std::string, Parameter> &replacements;

    using IRMutator::visit;

    void visit(const Load *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            expr = Load::make(op->type, op->name, mutate(op->index), op->image, i->second);
        } else {
            IRMutator::visit(op);
        }
    }

    void visit(const Store *op) {
        auto i = replacements.find(op->name);
        if (i != replacements.end()) {
            stmt = Store::make(op->name, mutate(op->value), mutate(op->index), i->second);
        } else {
            IRMutator::visit(op);
        }
    }

public:
    ReplaceParams(const std::map<std::string, Parameter> &replacements)
        : replacements(replacements) {}
};

Stmt replace_params(Stmt s, const std::map<std::string, Parameter> &replacements) {
    return ReplaceParams(replacements).mutate(s);
}

class InjectHexagonRpc : public IRMutator {
    std::map<std::string, Expr> state_vars;

    Module device_code;

    /** Alignment info for Int(32) variables in scope, so we don't
     * lose the information when creating Hexagon kernels. */
    Scope<ModulusRemainder> alignment_info;

    Expr state_var(const std::string& name, Type type) {
        Expr& var = state_vars[name];
        if (!var.defined()) {
            Buffer storage(type, {}, nullptr, name + "_buf");
            *(void **)storage.host_ptr() = nullptr;
            var = Load::make(type_of<void*>(), name + "_buf", 0, storage, Parameter());
        }
        return var;
    }

    Expr state_var_ptr(const std::string& name, Type type) {
        Expr var = state_var(name, type);
        return Call::make(Handle(), Call::address_of, {var}, Call::Intrinsic);
    }

    Expr module_state() {
        return state_var("hexagon_module_state", type_of<void*>());
    }

    Expr module_state_ptr() {
        return state_var_ptr("hexagon_module_state", type_of<void*>());
    }

    // Create a Buffer containing the given buffer/size, and return an
    // expression for a pointer to the first element.
    Expr buffer_ptr(const uint8_t* buffer, size_t size, const char* name) {
        Buffer code(type_of<uint8_t>(), {(int)size}, nullptr, name);
        memcpy(code.host_ptr(), buffer, (int)size);

        Expr ptr_0 = Load::make(type_of<uint8_t>(), name, 0, code, Parameter());
        return Call::make(Handle(), Call::address_of, {ptr_0}, Call::Intrinsic);
    }

    using IRMutator::visit;

    void visit(const For *loop) {
        if (loop->device_api == DeviceAPI::Hexagon) {
            // Unrolling or loop partitioning might generate multiple
            // loops with the same name, so we need to make them unique.
            std::string hex_name = unique_name("hex_" + loop->name);

            // After moving this to Hexagon, it doesn't need to be
            // marked Hexagon anymore.
            Stmt body = For::make(loop->name, loop->min, loop->extent, loop->for_type,
                                  DeviceAPI::None, loop->body);
            body = remove_trivial_for_loops(body);

            // Build a closure for the device code.
            // TODO: Should this move the body of the loop to Hexagon,
            // or the loop itself? Currently, this moves the loop itself.
            Closure c(body);

            // Make an argument list, and generate a function in the
            // device_code module. The hexagon runtime code expects
            // the arguments to appear in the order of (input buffers,
            // output buffers, input scalars).  Scalars must be last
            // for the scalar arguments to shadow the symbols of the
            // buffer that get generated by CodeGen_LLVM.
            std::vector<LoweredArgument> input_buffers, output_buffers;
            std::map<std::string, Parameter> replacement_params;
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    Argument::Kind kind = Argument::OutputBuffer;
                    output_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                } else {
                    Argument::Kind kind = Argument::InputBuffer;
                    input_buffers.push_back(LoweredArgument(i.first, kind, i.second.type, i.second.dimensions));
                }

                // Build a parameter to replace.
                Parameter p(i.second.type, true, i.second.dimensions);
                // Assert that buffers are aligned to one HVX vector.
                const int alignment = 128;
                p.set_host_alignment(alignment);
                // The other parameter constraints are already
                // accounted for by the closure grabbing those
                // arguments, so we only need to provide the host
                // alignment.
                replacement_params[i.first] = p;

                // Add an assert to the body that validates the
                // alignment of the buffer.
                if (!device_code.target().has_feature(Target::NoAsserts)) {
                    Expr host_ptr = reinterpret<uint64_t>(Variable::make(Handle(), i.first + ".host"));
                    Expr error = Call::make(Int(32), "halide_error_unaligned_host_ptr",
                                            {i.first, alignment}, Call::Extern);
                    body = Block::make(AssertStmt::make(host_ptr % alignment == 0, error), body);
                }
            }
            body = replace_params(body, replacement_params);

            std::vector<LoweredArgument> args;
            args.insert(args.end(), input_buffers.begin(), input_buffers.end());
            args.insert(args.end(), output_buffers.begin(), output_buffers.end());
            for (const auto& i : c.vars) {
                LoweredArgument arg(i.first, Argument::InputScalar, i.second, 0);
                if (alignment_info.contains(i.first)) {
                    arg.alignment = alignment_info.get(i.first);
                }
                args.push_back(arg);
            }
            device_code.append(LoweredFunc(hex_name, args, body, LoweredFunc::External));

            // Generate a call to hexagon_device_run.
            std::vector<Expr> arg_sizes;
            std::vector<Expr> arg_ptrs;
            std::vector<Expr> arg_flags;

            for (const auto& i : c.buffers) {
                arg_sizes.push_back(Expr((uint64_t) sizeof(buffer_t*)));
                arg_ptrs.push_back(Variable::make(type_of<buffer_t *>(), i.first + ".buffer"));
                // In the flags parameter, bit 0 set indicates the
                // buffer is read, bit 1 set indicates the buffer is
                // written. If neither are set, the argument is a scalar.
                int flags = 0;
                if (i.second.read) flags |= 0x1;
                if (i.second.write) flags |= 0x2;
                arg_flags.push_back(flags);
            }
            for (const auto& i : c.vars) {
                Expr arg = Variable::make(i.second, i.first);
                Expr arg_ptr = Call::make(type_of<void *>(), Call::make_struct, {arg}, Call::Intrinsic);

                // sizeof(scalar-type) will always fit into int32
                arg_sizes.push_back(Expr((uint64_t) i.second.bytes()));
                arg_ptrs.push_back(arg_ptr);
                arg_flags.push_back(0x0);
            }

            // The argument list is terminated with an argument of size 0.
            arg_sizes.push_back(Expr((uint64_t) 0));

            std::string pipeline_name = hex_name + "_argv";
            std::vector<Expr> params;
            params.push_back(module_state());
            params.push_back(pipeline_name);
            params.push_back(state_var_ptr(hex_name, type_of<int>()));
            params.push_back(Call::make(type_of<size_t*>(), Call::make_struct, arg_sizes, Call::Intrinsic));
            params.push_back(Call::make(type_of<void**>(), Call::make_struct, arg_ptrs, Call::Intrinsic));
            params.push_back(Call::make(type_of<int*>(), Call::make_struct, arg_flags, Call::Intrinsic));

            stmt = call_extern_and_assert("halide_hexagon_run", params);

        } else {
            IRMutator::visit(loop);
        }
    }

    void visit(const Let *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

    void visit(const LetStmt *op) {
        if (op->value.type() == Int(32)) {
            alignment_info.push(op->name, modulus_remainder(op->value, alignment_info));
        }

        IRMutator::visit(op);

        if (op->value.type() == Int(32)) {
            alignment_info.pop(op->name);
        }
    }

public:
    InjectHexagonRpc(const Target &target) : device_code("hexagon", target) {}

    Stmt inject(Stmt s) {
        s = mutate(s);

        // Skip if there are no device kernels.
        if (device_code.functions().empty()) {
            return s;
        }

        // Compile the device code.
        // TODO: Currently, this requires shelling out to
        // hexagon-clang from the Qualcomm Hexagon SDK, because the
        // Hexagon LLVM target is not fully open source yet. When the
        // LLVM Hexagon target is fully open sourced, we can instead
        // just compile the module to an object, and find a way to
        // link it to a shared object.
        debug(1) << "Hexagon device code module: " << device_code << "\n";

        // First compile the module to an llvm module
        llvm::LLVMContext context;
        std::unique_ptr<llvm::Module> llvm_module =
            compile_module_to_llvm_module(device_code, context);

        #if LLVM_VERSION >= 39
        // Then mess with it, to fix up version differences between
        // our LLVM and hexagon-clang. Yuck.
        for (auto &gv : llvm_module->globals()) {
            // hexagon-clang doesn't understand the local_unnamed_addr
            // attribute, so we must strip it.
            gv.setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::None);
        }
        for (auto &fn : llvm_module->functions()) {
            fn.setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::None);
        }
        #endif

        // Dump the llvm module to a temp file as .ll
        TemporaryFile tmp_bitcode("hex", ".ll");
        TemporaryFile tmp_shared_object("hex", ".o");
        std::unique_ptr<llvm::raw_fd_ostream> ostream =
            make_raw_fd_ostream(tmp_bitcode.pathname());
        compile_llvm_module_to_llvm_assembly(*llvm_module, *ostream);
        ostream->flush();

        // Shell out to hexagon clang to compile it.
        string hex_command;

        const char *path = getenv("HL_HEXAGON_CLANG");
        if (path && path[0]) {
            hex_command = path;
        } else {
            path = getenv("HL_HEXAGON_TOOLS");
            if (path && path[0]) {
                hex_command = string(path) + "/bin/hexagon-clang";
            } else {
                user_error << "Unable to find hexagon-clang: neither HL_HEXAGON_CLANG nor HL_HEXAGON_TOOLS are set properly.";
            }
        }

        hex_command += " -c ";
        hex_command += tmp_bitcode.pathname();
        hex_command += " -fno-pic -mlong-calls -O3 -mllvm -lsr-complexity-limit=65535 -Wno-override-module ";
        if (device_code.target().has_feature(Target::HVX_v62)) {
            hex_command += " -mv62";
        }
        if (device_code.target().has_feature(Target::HVX_128)) {
            hex_command += " -mhvx-double";
        }
        hex_command += " -o " + tmp_shared_object.pathname();
        int result = system(hex_command.c_str());
        internal_assert(result == 0) << "hexagon-clang failed\n";

        // Read the compiled object back in and put it in a buffer in the module
        std::ifstream so(tmp_shared_object.pathname(), std::ios::binary | std::ios::ate);
        internal_assert(so.good()) << "failed to open temporary shared object.";
        std::vector<uint8_t> object(so.tellg());
        so.seekg(0, std::ios::beg);
        so.read(reinterpret_cast<char*>(&object[0]), object.size());

        size_t code_size = object.size();
        Expr code_ptr = buffer_ptr(&object[0], code_size, "hexagon_code");

        // Wrap the statement in calls to halide_initialize_kernels.
        Stmt init_kernels = call_extern_and_assert("halide_hexagon_initialize_kernels",
                                                   {module_state_ptr(), code_ptr, Expr((uint64_t) code_size)});
        s = Block::make(init_kernels, s);

        return s;
    }
};

}

Stmt inject_hexagon_rpc(Stmt s, const Target &host_target) {
    // Make a new target for the device module.
    Target target(Target::NoOS, Target::Hexagon, 32);

    // These feature flags are propagated from the host target to the
    // device module.
    //
    // TODO: We'd like Target::Debug to be in this list too, but trunk
    // llvm currently disagrees with hexagon clang as to what
    // constitutes valid debug info.
    static const Target::Feature shared_features[] = {
        Target::NoAsserts,
        Target::HVX_64,
        Target::HVX_128,
        Target::HVX_v62
    };
    for (Target::Feature i : shared_features) {
        if (host_target.has_feature(i)) {
            target = target.with_feature(i);
        }
    }

    InjectHexagonRpc injector(target);
    s = injector.inject(s);
    return s;
}

}  // namespace Internal
}  // namespace Halide
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
/*=========================================================================
 *
 *  Portions of this file are subject to the VTK Toolkit Version 3 copyright.
 *
 *  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 *
 *  For complete copyright, license and disclaimer of warranty information
 *  please refer to the NOTICE file at the top of the ITK source tree.
 *
 *=========================================================================*/
#include "itkLSMImageIO.h"
#include "itkByteSwapper.h"

#include "itk_kwiml.h"
#include "itk_tiff.h"

/* Structure with LSM-specific data ( only in the first image directory). */
#define TIF_CZ_LSMINFO 34412 /* 0x866c, Type: TIF_BYTE, Length: 512 */
#define TIF_CZ_LSMINFO_SIZE_RESERVED 90 + 6
#define TIF_CZ_LSMINFO_SIZE 512

namespace itk
{
extern "C"
{
static void TagExtender(TIFF *tiff)
{
  static ITK_CONSTEXPR_VAR TIFFFieldInfo xtiffFieldInfo[] = {
          { TIF_CZ_LSMINFO, TIFF_VARIABLE, TIFF_VARIABLE, TIFF_BYTE,
          FIELD_CUSTOM, 0, 1, const_cast< char * >( "LSM Private Tag" ) }
    };

  TIFFMergeFieldInfo( tiff, xtiffFieldInfo,
                      sizeof( xtiffFieldInfo ) / sizeof( xtiffFieldInfo[0] ) );
}
}

typedef KWIML_INT_int32_t  Int32_t;
typedef KWIML_INT_uint32_t UInt32_t;

typedef float       Float32_t;
typedef double      Float64_t;
typedef long double Float96_t;

typedef struct {
  UInt32_t U32MagicNumber;
  Int32_t S32StructureSize;
  Int32_t S32DimensionX;
  Int32_t S32DimensionY;
  Int32_t S32DimensionZ;
  Int32_t S32DimensionChannels;
  Int32_t S32DimensionTime;
  Int32_t S32DataType;
  Int32_t S32ThumbnailX;
  Int32_t S32ThumbnailY;
  Float64_t F64VoxelSizeX;
  Float64_t F64VoxelSizeY;
  Float64_t F64VoxelSizeZ;
  UInt32_t u32ScanType;
  UInt32_t u32DataType;
  UInt32_t u32OffsetVectorOverlay;
  UInt32_t u32OffsetInputLut;
  UInt32_t u32OffsetOutputLut;
  UInt32_t u32OffsetChannelColors;
  Float64_t F64TimeIntervall;
  UInt32_t u32OffsetChannelDataTypes;
  UInt32_t u32OffsetScanInformation;
  UInt32_t u32OffsetKsData;
  UInt32_t u32OffsetTimeStamps;
  UInt32_t u32OffsetEventList;
  UInt32_t u32OffsetRoi;
  UInt32_t u32OffsetBleachRoi;
  UInt32_t u32OffsetNextRecording;
  UInt32_t u32Reserved[TIF_CZ_LSMINFO_SIZE_RESERVED];
} zeiss_info;

LSMImageIO::LSMImageIO()
{
  m_ByteOrder = LittleEndian;
  m_FileType = Binary;

  this->AddSupportedWriteExtension(".lsm");
  this->AddSupportedWriteExtension(".LSM");

  this->AddSupportedReadExtension(".lsm");
  this->AddSupportedReadExtension(".LSM");
}

LSMImageIO::~LSMImageIO()
{}

// This method will only test if the header looks like a
// LSM image file.
bool LSMImageIO::CanReadFile(const char *filename)
{
  std::string   fname(filename);

  if ( fname == "" )
    {
    itkDebugMacro(<< "No filename specified.");
    return false;
    }

  bool                   extensionFound = false;
  std::string::size_type sprPos = fname.rfind(".lsm");
  if ( ( sprPos != std::string::npos )
       && ( sprPos == fname.length() - 4 ) )
    {
    extensionFound = true;
    }
  sprPos = fname.rfind(".LSM");
  if ( ( sprPos != std::string::npos )
       && ( sprPos == fname.length() - 4 ) )
    {
    extensionFound = true;
    }

  if ( !extensionFound )
    {
    itkDebugMacro(<< "The filename extension is not recognized");
    return false;
    }

  // Check that TIFFImageIO can read this file:
  TIFFErrorHandler save = TIFFSetWarningHandler(ITK_NULLPTR); // get rid of warning about
                                                    // Zeiss tag
  if ( !this->TIFFImageIO::CanReadFile(filename) )
    {
    return false;
    }
  TIFFSetWarningHandler(save);

  // Check this is indeed a LSM file
  if ( !this->CanFindTIFFTag(TIF_CZ_LSMINFO) )
    {
    return false;
    }

  // everything was ok
  return true;
}

void LSMImageIO::Read(void *buffer)
{
  this->TIFFImageIO::Read(buffer);
}

void LSMImageIO::ReadImageInformation()
{
  // this really should be a compile time assert
  itkAssertInDebugAndIgnoreInReleaseMacro( sizeof( zeiss_info ) == TIF_CZ_LSMINFO_SIZE );

  this->TIFFImageIO::ReadImageInformation();

  // Now is a good time to check what was read and replaced it with LSM
  // information
  unsigned int tif_cz_lsminfo_size;
  void *praw = this->TIFFImageIO::ReadRawByteFromTag(TIF_CZ_LSMINFO, tif_cz_lsminfo_size);
  zeiss_info *zi = reinterpret_cast< zeiss_info * >( praw );
  if ( praw == ITK_NULLPTR
       || tif_cz_lsminfo_size != TIF_CZ_LSMINFO_SIZE )
    {
    // no zeiss info, just use tiff spacing
    return;
    }
  // FIXME byte swap, when should it happen? writting?
  ByteSwapper<double>::SwapFromSystemToLittleEndian( &zi->F64VoxelSizeX );
  ByteSwapper<double>::SwapFromSystemToLittleEndian( &zi->F64VoxelSizeY );
  ByteSwapper<double>::SwapFromSystemToLittleEndian( &zi->F64VoxelSizeZ );
  m_Spacing[0] = zi->F64VoxelSizeX;
  m_Spacing[1] = zi->F64VoxelSizeY;
  // TIFF only support 2 or 3 dimension:
  if ( m_NumberOfDimensions == 3 )
    {
    m_Spacing[2] = zi->F64VoxelSizeZ;
    }
}

bool LSMImageIO::CanWriteFile(const char *name)
{
  std::string filename = name;

  if ( filename == "" )
    {
    return false;
    }

  std::string::size_type pos = filename.rfind(".lsm");
  if ( ( pos != std::string::npos )
       && ( pos == filename.length() - 4 ) )
    {
    return true;
    }

  pos = filename.rfind(".LSM");
  if ( ( pos != std::string::npos )
       && ( pos == filename.length() - 4 ) )
    {
    return true;
    }

  return false;
}

void LSMImageIO::FillZeissStruct(char *cz)
{
  memset(cz, 0, TIF_CZ_LSMINFO_SIZE); // fill with 0
  zeiss_info *z = reinterpret_cast< zeiss_info * >( cz );
  z->U32MagicNumber = 0x0400494c;
  z->S32StructureSize = TIF_CZ_LSMINFO_SIZE;
  z->S32DimensionX = m_Dimensions[0];
  z->S32DimensionY = m_Dimensions[1];
  if ( m_NumberOfDimensions == 3 )
    {
    z->S32DimensionZ = m_Dimensions[2];
    }
  z->S32DimensionChannels = m_NumberOfComponents;
  z->S32DimensionTime = 1;
  z->S32DataType = 0;
  z->S32ThumbnailX = 128 * m_Dimensions[0] / m_Dimensions[1];
  z->S32ThumbnailY = 128;
  z->F64VoxelSizeX = m_Spacing[0];
  z->F64VoxelSizeY = m_Spacing[1];
  if ( m_NumberOfDimensions == 3 )
    {
    z->F64VoxelSizeZ = m_Spacing[2];
    }
}

void LSMImageIO::Write(const void *buffer)
{
  const unsigned char *outPtr = (const unsigned char *)buffer;

  unsigned int width, height, page, pages = 1;
  if ( this->GetNumberOfDimensions() )
    {
    itkExceptionMacro("TIFF requires images to have at least 2 dimensions");
    }
  width =  m_Dimensions[0];
  height = m_Dimensions[1];
  if ( m_NumberOfDimensions == 3 )
    {
    pages = m_Dimensions[2];
    }

  uint16_t    scomponents = this->GetNumberOfComponents();
  float resolution = -1;
  uint16_t    bps;

  switch ( this->GetComponentType() )
    {
    case UCHAR:
      bps = 8;
      break;

    case USHORT:
      bps = 16;
      break;

    default:
      itkExceptionMacro(<< "TIFF supports unsigned char and unsigned short");
    }

  uint16_t predictor;

  TIFF *tif = TIFFOpen(m_FileName.c_str(), "w");
  if ( !tif )
    {
    itkDebugMacro(<< "Returning");
    return;
    }

  uint32 w = width;
  uint32 h = height;

  TIFFSetTagExtender(TagExtender);
  if ( m_NumberOfDimensions == 3 )
    {
    TIFFCreateDirectory(tif);
    }
  for ( page = 0; page < pages; page++ )
    {
    TIFFSetDirectory(tif, page);
    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
    TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, scomponents);
    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps); // Fix for stype
    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    char zeiss[TIF_CZ_LSMINFO_SIZE];
    FillZeissStruct(zeiss);
    unsigned int iCount = sizeof( zeiss ) / sizeof( zeiss[0] );
    // Zeiss field is only on the first TIFF image
    if ( page == 0 )
      {
      TIFFSetField(tif, TIF_CZ_LSMINFO, iCount, zeiss);
      }

    if ( scomponents > 3 )
      {
      // if number of scalar components is greater than 3, that means we assume
      // there is alpha.
      uint16  extra_samples = scomponents - 3;
      uint16 *sample_info = new uint16[scomponents - 3];
      sample_info[0] = EXTRASAMPLE_ASSOCALPHA;
      int cc;
      for ( cc = 1; cc < scomponents - 3; cc++ )
        {
        sample_info[cc] = EXTRASAMPLE_UNSPECIFIED;
        }
      TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, extra_samples,
                   sample_info);
      delete[] sample_info;
      }

    uint16_t compression;

    if ( m_UseCompression )
      {
      switch ( m_Compression )
        {
        case TIFFImageIO::PackBits:
          compression = COMPRESSION_PACKBITS; break;
        case TIFFImageIO::JPEG:
          compression = COMPRESSION_JPEG; break;
        case TIFFImageIO::Deflate:
          compression = COMPRESSION_DEFLATE; break;
        case TIFFImageIO::LZW:
          compression = COMPRESSION_LZW; break;
        default:
          compression = COMPRESSION_NONE;
        }
      }
    else
      {
      compression = COMPRESSION_NONE;
      }

    TIFFSetField(tif, TIFFTAG_COMPRESSION, compression); // Fix for compression

    uint16 photometric = ( scomponents == 1 ) ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB;

    if ( compression == COMPRESSION_JPEG )
      {
      TIFFSetField(tif, TIFFTAG_JPEGQUALITY, 75); // Parameter
      TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
      photometric = PHOTOMETRIC_YCBCR;
      }
    else if ( compression == COMPRESSION_LZW )
      {
      predictor = 2;
      TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor);
      itkDebugMacro(<< "LZW compression is patented outside US so it is disabled");
      }
    else if ( compression == COMPRESSION_DEFLATE )
      {
      predictor = 2;
      TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor);
      }

    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric); // Fix for scomponents

    if ( resolution > 0 )
      {
      TIFFSetField(tif, TIFFTAG_XRESOLUTION, resolution);
      TIFFSetField(tif, TIFFTAG_YRESOLUTION, resolution);
      TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
      }

    if ( m_NumberOfDimensions == 3 )
      {
      // We are writing single page of the multipage file
      TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
      }
    int rowLength; // in bytes

    switch ( this->GetComponentType() )
      {
      case UCHAR:
        rowLength = sizeof( unsigned char );
        break;
      case USHORT:
        rowLength = sizeof( unsigned short );
        break;
      default:
        itkExceptionMacro(<< "TIFF supports unsigned char and unsigned short");
      }

    rowLength *= this->GetNumberOfComponents();
    rowLength *= width;

    int row = 0;
    for ( unsigned int idx2 = 0; idx2 < height; idx2++ )
      {
      if ( TIFFWriteScanline(tif, const_cast< unsigned char * >( outPtr ), row, 0) < 0 )
        {
        itkExceptionMacro(<< "TIFFImageIO: error out of disk space");
        }
      outPtr += rowLength;
      row++;
      }

    if ( m_NumberOfDimensions == 3 )
      {
      TIFFWriteDirectory(tif);
      }
    }
  TIFFClose(tif);
}

void LSMImageIO::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
}
} // end namespace itk
<commit_msg>BUG: LSMImageIO was not checking correctly image dimension<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
/*=========================================================================
 *
 *  Portions of this file are subject to the VTK Toolkit Version 3 copyright.
 *
 *  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 *
 *  For complete copyright, license and disclaimer of warranty information
 *  please refer to the NOTICE file at the top of the ITK source tree.
 *
 *=========================================================================*/
#include "itkLSMImageIO.h"
#include "itkByteSwapper.h"

#include "itk_kwiml.h"
#include "itk_tiff.h"

/* Structure with LSM-specific data ( only in the first image directory). */
#define TIF_CZ_LSMINFO 34412 /* 0x866c, Type: TIF_BYTE, Length: 512 */
#define TIF_CZ_LSMINFO_SIZE_RESERVED 90 + 6
#define TIF_CZ_LSMINFO_SIZE 512

namespace itk
{
extern "C"
{
static void TagExtender(TIFF *tiff)
{
  static ITK_CONSTEXPR_VAR TIFFFieldInfo xtiffFieldInfo[] = {
          { TIF_CZ_LSMINFO, TIFF_VARIABLE, TIFF_VARIABLE, TIFF_BYTE,
          FIELD_CUSTOM, 0, 1, const_cast< char * >( "LSM Private Tag" ) }
    };

  TIFFMergeFieldInfo( tiff, xtiffFieldInfo,
                      sizeof( xtiffFieldInfo ) / sizeof( xtiffFieldInfo[0] ) );
}
}

typedef KWIML_INT_int32_t  Int32_t;
typedef KWIML_INT_uint32_t UInt32_t;

typedef float       Float32_t;
typedef double      Float64_t;
typedef long double Float96_t;

typedef struct {
  UInt32_t U32MagicNumber;
  Int32_t S32StructureSize;
  Int32_t S32DimensionX;
  Int32_t S32DimensionY;
  Int32_t S32DimensionZ;
  Int32_t S32DimensionChannels;
  Int32_t S32DimensionTime;
  Int32_t S32DataType;
  Int32_t S32ThumbnailX;
  Int32_t S32ThumbnailY;
  Float64_t F64VoxelSizeX;
  Float64_t F64VoxelSizeY;
  Float64_t F64VoxelSizeZ;
  UInt32_t u32ScanType;
  UInt32_t u32DataType;
  UInt32_t u32OffsetVectorOverlay;
  UInt32_t u32OffsetInputLut;
  UInt32_t u32OffsetOutputLut;
  UInt32_t u32OffsetChannelColors;
  Float64_t F64TimeIntervall;
  UInt32_t u32OffsetChannelDataTypes;
  UInt32_t u32OffsetScanInformation;
  UInt32_t u32OffsetKsData;
  UInt32_t u32OffsetTimeStamps;
  UInt32_t u32OffsetEventList;
  UInt32_t u32OffsetRoi;
  UInt32_t u32OffsetBleachRoi;
  UInt32_t u32OffsetNextRecording;
  UInt32_t u32Reserved[TIF_CZ_LSMINFO_SIZE_RESERVED];
} zeiss_info;

LSMImageIO::LSMImageIO()
{
  m_ByteOrder = LittleEndian;
  m_FileType = Binary;

  this->AddSupportedWriteExtension(".lsm");
  this->AddSupportedWriteExtension(".LSM");

  this->AddSupportedReadExtension(".lsm");
  this->AddSupportedReadExtension(".LSM");
}

LSMImageIO::~LSMImageIO()
{}

// This method will only test if the header looks like a
// LSM image file.
bool LSMImageIO::CanReadFile(const char *filename)
{
  std::string   fname(filename);

  if ( fname == "" )
    {
    itkDebugMacro(<< "No filename specified.");
    return false;
    }

  bool                   extensionFound = false;
  std::string::size_type sprPos = fname.rfind(".lsm");
  if ( ( sprPos != std::string::npos )
       && ( sprPos == fname.length() - 4 ) )
    {
    extensionFound = true;
    }
  sprPos = fname.rfind(".LSM");
  if ( ( sprPos != std::string::npos )
       && ( sprPos == fname.length() - 4 ) )
    {
    extensionFound = true;
    }

  if ( !extensionFound )
    {
    itkDebugMacro(<< "The filename extension is not recognized");
    return false;
    }

  // Check that TIFFImageIO can read this file:
  TIFFErrorHandler save = TIFFSetWarningHandler(ITK_NULLPTR); // get rid of warning about
                                                    // Zeiss tag
  if ( !this->TIFFImageIO::CanReadFile(filename) )
    {
    return false;
    }
  TIFFSetWarningHandler(save);

  // Check this is indeed a LSM file
  if ( !this->CanFindTIFFTag(TIF_CZ_LSMINFO) )
    {
    return false;
    }

  // everything was ok
  return true;
}

void LSMImageIO::Read(void *buffer)
{
  this->TIFFImageIO::Read(buffer);
}

void LSMImageIO::ReadImageInformation()
{
  // this really should be a compile time assert
  itkAssertInDebugAndIgnoreInReleaseMacro( sizeof( zeiss_info ) == TIF_CZ_LSMINFO_SIZE );

  this->TIFFImageIO::ReadImageInformation();

  // Now is a good time to check what was read and replaced it with LSM
  // information
  unsigned int tif_cz_lsminfo_size;
  void *praw = this->TIFFImageIO::ReadRawByteFromTag(TIF_CZ_LSMINFO, tif_cz_lsminfo_size);
  zeiss_info *zi = reinterpret_cast< zeiss_info * >( praw );
  if ( praw == ITK_NULLPTR
       || tif_cz_lsminfo_size != TIF_CZ_LSMINFO_SIZE )
    {
    // no zeiss info, just use tiff spacing
    return;
    }
  // FIXME byte swap, when should it happen? writting?
  ByteSwapper<double>::SwapFromSystemToLittleEndian( &zi->F64VoxelSizeX );
  ByteSwapper<double>::SwapFromSystemToLittleEndian( &zi->F64VoxelSizeY );
  ByteSwapper<double>::SwapFromSystemToLittleEndian( &zi->F64VoxelSizeZ );
  m_Spacing[0] = zi->F64VoxelSizeX;
  m_Spacing[1] = zi->F64VoxelSizeY;
  // TIFF only support 2 or 3 dimension:
  if ( m_NumberOfDimensions == 3 )
    {
    m_Spacing[2] = zi->F64VoxelSizeZ;
    }
}

bool LSMImageIO::CanWriteFile(const char *name)
{
  std::string filename = name;

  if ( filename == "" )
    {
    return false;
    }

  std::string::size_type pos = filename.rfind(".lsm");
  if ( ( pos != std::string::npos )
       && ( pos == filename.length() - 4 ) )
    {
    return true;
    }

  pos = filename.rfind(".LSM");
  if ( ( pos != std::string::npos )
       && ( pos == filename.length() - 4 ) )
    {
    return true;
    }

  return false;
}

void LSMImageIO::FillZeissStruct(char *cz)
{
  memset(cz, 0, TIF_CZ_LSMINFO_SIZE); // fill with 0
  zeiss_info *z = reinterpret_cast< zeiss_info * >( cz );
  z->U32MagicNumber = 0x0400494c;
  z->S32StructureSize = TIF_CZ_LSMINFO_SIZE;
  z->S32DimensionX = m_Dimensions[0];
  z->S32DimensionY = m_Dimensions[1];
  if ( m_NumberOfDimensions == 3 )
    {
    z->S32DimensionZ = m_Dimensions[2];
    }
  z->S32DimensionChannels = m_NumberOfComponents;
  z->S32DimensionTime = 1;
  z->S32DataType = 0;
  z->S32ThumbnailX = 128 * m_Dimensions[0] / m_Dimensions[1];
  z->S32ThumbnailY = 128;
  z->F64VoxelSizeX = m_Spacing[0];
  z->F64VoxelSizeY = m_Spacing[1];
  if ( m_NumberOfDimensions == 3 )
    {
    z->F64VoxelSizeZ = m_Spacing[2];
    }
}

void LSMImageIO::Write(const void *buffer)
{
  const unsigned char *outPtr = (const unsigned char *)buffer;

  unsigned int width, height, page, pages = 1;
  if ( this->GetNumberOfDimensions() < 2 )
    {
    itkExceptionMacro("TIFF requires images to have at least 2 dimensions");
    }
  width =  m_Dimensions[0];
  height = m_Dimensions[1];
  if ( m_NumberOfDimensions == 3 )
    {
    pages = m_Dimensions[2];
    }

  uint16_t    scomponents = this->GetNumberOfComponents();
  float resolution = -1;
  uint16_t    bps;

  switch ( this->GetComponentType() )
    {
    case UCHAR:
      bps = 8;
      break;

    case USHORT:
      bps = 16;
      break;

    default:
      itkExceptionMacro(<< "TIFF supports unsigned char and unsigned short");
    }

  uint16_t predictor;

  TIFF *tif = TIFFOpen(m_FileName.c_str(), "w");
  if ( !tif )
    {
    itkDebugMacro(<< "Returning");
    return;
    }

  uint32 w = width;
  uint32 h = height;

  TIFFSetTagExtender(TagExtender);
  if ( m_NumberOfDimensions == 3 )
    {
    TIFFCreateDirectory(tif);
    }
  for ( page = 0; page < pages; page++ )
    {
    TIFFSetDirectory(tif, page);
    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
    TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, scomponents);
    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps); // Fix for stype
    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    char zeiss[TIF_CZ_LSMINFO_SIZE];
    FillZeissStruct(zeiss);
    unsigned int iCount = sizeof( zeiss ) / sizeof( zeiss[0] );
    // Zeiss field is only on the first TIFF image
    if ( page == 0 )
      {
      TIFFSetField(tif, TIF_CZ_LSMINFO, iCount, zeiss);
      }

    if ( scomponents > 3 )
      {
      // if number of scalar components is greater than 3, that means we assume
      // there is alpha.
      uint16  extra_samples = scomponents - 3;
      uint16 *sample_info = new uint16[scomponents - 3];
      sample_info[0] = EXTRASAMPLE_ASSOCALPHA;
      int cc;
      for ( cc = 1; cc < scomponents - 3; cc++ )
        {
        sample_info[cc] = EXTRASAMPLE_UNSPECIFIED;
        }
      TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, extra_samples,
                   sample_info);
      delete[] sample_info;
      }

    uint16_t compression;

    if ( m_UseCompression )
      {
      switch ( m_Compression )
        {
        case TIFFImageIO::PackBits:
          compression = COMPRESSION_PACKBITS; break;
        case TIFFImageIO::JPEG:
          compression = COMPRESSION_JPEG; break;
        case TIFFImageIO::Deflate:
          compression = COMPRESSION_DEFLATE; break;
        case TIFFImageIO::LZW:
          compression = COMPRESSION_LZW; break;
        default:
          compression = COMPRESSION_NONE;
        }
      }
    else
      {
      compression = COMPRESSION_NONE;
      }

    TIFFSetField(tif, TIFFTAG_COMPRESSION, compression); // Fix for compression

    uint16 photometric = ( scomponents == 1 ) ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB;

    if ( compression == COMPRESSION_JPEG )
      {
      TIFFSetField(tif, TIFFTAG_JPEGQUALITY, 75); // Parameter
      TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
      photometric = PHOTOMETRIC_YCBCR;
      }
    else if ( compression == COMPRESSION_LZW )
      {
      predictor = 2;
      TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor);
      itkDebugMacro(<< "LZW compression is patented outside US so it is disabled");
      }
    else if ( compression == COMPRESSION_DEFLATE )
      {
      predictor = 2;
      TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor);
      }

    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric); // Fix for scomponents

    if ( resolution > 0 )
      {
      TIFFSetField(tif, TIFFTAG_XRESOLUTION, resolution);
      TIFFSetField(tif, TIFFTAG_YRESOLUTION, resolution);
      TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
      }

    if ( m_NumberOfDimensions == 3 )
      {
      // We are writing single page of the multipage file
      TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
      }
    int rowLength; // in bytes

    switch ( this->GetComponentType() )
      {
      case UCHAR:
        rowLength = sizeof( unsigned char );
        break;
      case USHORT:
        rowLength = sizeof( unsigned short );
        break;
      default:
        itkExceptionMacro(<< "TIFF supports unsigned char and unsigned short");
      }

    rowLength *= this->GetNumberOfComponents();
    rowLength *= width;

    int row = 0;
    for ( unsigned int idx2 = 0; idx2 < height; idx2++ )
      {
      if ( TIFFWriteScanline(tif, const_cast< unsigned char * >( outPtr ), row, 0) < 0 )
        {
        itkExceptionMacro(<< "TIFFImageIO: error out of disk space");
        }
      outPtr += rowLength;
      row++;
      }

    if ( m_NumberOfDimensions == 3 )
      {
      TIFFWriteDirectory(tif);
      }
    }
  TIFFClose(tif);
}

void LSMImageIO::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
}
} // end namespace itk
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/instant/instant_field_trials.h"

#include "base/metrics/field_trial.h"
#include "chrome/common/metrics/variations/variation_ids.h"
#include "chrome/common/metrics/variations/variations_util.h"

namespace instant {

void SetupInstantFieldTrials() {
  const base::FieldTrial::Probability num_buckets = 1000;
  // Default to 5%.  This number may be overridden by the Variations server.
  const base::FieldTrial::Probability group_probability = 50;
  const std::string trial_name = "InstantDummy";
  const std::string default_group_name = "DefaultGroup";
  const std::string control_group_name = "Control";
  const std::string experiment_one_group_name = "Experiment1";
  const std::string experiment_two_group_name = "Experiment2";
  scoped_refptr<base::FieldTrial> trial(
      base::FieldTrialList::FactoryGetFieldTrial(
          trial_name, num_buckets, default_group_name,
          2013, 6, 30, NULL));

  // Create field trial groups.
  trial->AppendGroup(control_group_name, group_probability);
  trial->AppendGroup(experiment_one_group_name, group_probability);
  trial->AppendGroup(experiment_two_group_name, group_probability);

  // Setup Google Variation IDs for each group in the field trial so Google
  // servers are aware of the variants.
  chrome_variations::AssociateGoogleVariationID(trial_name, default_group_name,
      chrome_variations::kDummyInstantIDDefault);
  chrome_variations::AssociateGoogleVariationID(trial_name, control_group_name,
      chrome_variations::kDummyInstantIDControl);
  chrome_variations::AssociateGoogleVariationID(trial_name,
      experiment_one_group_name,
      chrome_variations::kDummyInstantIDExperimentOne);
  chrome_variations::AssociateGoogleVariationID(trial_name,
      experiment_two_group_name,
      chrome_variations::kDummyInstantIDExperimentTwo);
}

}  // namespace instant
<commit_msg>Make instant trials one time randomized.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/instant/instant_field_trials.h"

#include "base/metrics/field_trial.h"
#include "chrome/common/metrics/variations/variation_ids.h"
#include "chrome/common/metrics/variations/variations_util.h"

namespace instant {

void SetupInstantFieldTrials() {
  const base::FieldTrial::Probability num_buckets = 1000;
  // Default to 5%.  This number may be overridden by the Variations server.
  const base::FieldTrial::Probability group_probability = 50;
  const std::string trial_name = "InstantDummy";
  const std::string default_group_name = "DefaultGroup";
  const std::string control_group_name = "Control";
  const std::string experiment_one_group_name = "Experiment1";
  const std::string experiment_two_group_name = "Experiment2";
  scoped_refptr<base::FieldTrial> trial(
      base::FieldTrialList::FactoryGetFieldTrial(
          trial_name, num_buckets, default_group_name,
          2013, 6, 30, NULL));
  // Give users a consistent experience.
  if (base::FieldTrialList::IsOneTimeRandomizationEnabled())
    trial->UseOneTimeRandomization();

  // Create field trial groups.
  trial->AppendGroup(control_group_name, group_probability);
  trial->AppendGroup(experiment_one_group_name, group_probability);
  trial->AppendGroup(experiment_two_group_name, group_probability);

  // Setup Google Variation IDs for each group in the field trial so Google
  // servers are aware of the variants.
  chrome_variations::AssociateGoogleVariationID(trial_name, default_group_name,
      chrome_variations::kDummyInstantIDDefault);
  chrome_variations::AssociateGoogleVariationID(trial_name, control_group_name,
      chrome_variations::kDummyInstantIDControl);
  chrome_variations::AssociateGoogleVariationID(trial_name,
      experiment_one_group_name,
      chrome_variations::kDummyInstantIDExperimentOne);
  chrome_variations::AssociateGoogleVariationID(trial_name,
      experiment_two_group_name,
      chrome_variations::kDummyInstantIDExperimentTwo);
}

}  // namespace instant
<|endoftext|>


<commit_before>//
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2013 Project Chrono
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file at the top level of the distribution
// and at http://projectchrono.org/license-chrono.txt.
//

// A very simple example that can be used as template project for
// a Chrono::Engine simulator with 3D view.

#include "chrono/physics/ChSystem.h"
#include "chrono/physics/ChBodyEasy.h"
#include "chrono/physics/ChLinkMate.h"
#include "chrono/assets/ChTexture.h"
#include "chrono/assets/ChColorAsset.h"
#include "chrono_irrlicht/ChIrrApp.h"

// Use the namespace of Chrono

using namespace chrono;
using namespace chrono::irrlicht;

// Use the main namespaces of Irrlicht

using namespace irr;
using namespace irr::core;
using namespace irr::scene;
using namespace irr::video;
using namespace irr::io;
using namespace irr::gui;

int main(int argc, char* argv[]) {
    // Set path to Chrono data directory
    SetChronoDataPath(CHRONO_DATA_DIR);

    // Create a Chrono physical system
    ChSystem mphysicalSystem;

    // Create the Irrlicht visualization (open the Irrlicht device,
    // bind a simple user interface, etc. etc.)
    ChIrrApp application(&mphysicalSystem, L"A simple project template", core::dimension2d<u32>(800, 600),
                         false);  // screen dimensions

    // Easy shortcuts to add camera, lights, logo and sky in Irrlicht scene:
    application.AddTypicalLogo();
    application.AddTypicalSky();
    application.AddTypicalLights();
    application.AddTypicalCamera(core::vector3df(2, 2, -5),
                                 core::vector3df(0, 1, 0));  // to change the position of camera
    // application.AddLightWithShadow(vector3df(1,25,-5), vector3df(0,0,0), 35, 0.2,35, 55, 512, video::SColorf(1,1,1));

    //======================================================================

    // HERE YOU CAN POPULATE THE PHYSICAL SYSTEM WITH BODIES AND LINKS.
    //
    // An example: a pendulum.

    // 1-Create a floor that is fixed (that is used also to represent the absolute reference)

    auto floorBody = std::make_shared<ChBodyEasyBox>(10, 2, 10,  // x, y, z dimensions
                                                     3000,       // density
                                                     false,      // no contact geometry
                                                     true        // enable visualization geometry
                                                     );
    floorBody->SetPos(ChVector<>(0, -2, 0));
    floorBody->SetBodyFixed(true);

    mphysicalSystem.Add(floorBody);

    // 2-Create a pendulum

    auto pendulumBody = std::make_shared<ChBodyEasyBox>(0.5, 2, 0.5,  // x, y, z dimensions
                                                        3000,         // density
                                                        false,        // no contact geometry
                                                        true          // enable visualization geometry
                                                        );
    pendulumBody->SetPos(ChVector<>(0, 3, 0));
    pendulumBody->SetPos_dt(ChVector<>(1, 0, 0));

    mphysicalSystem.Add(pendulumBody);

    // 3-Create a spherical constraint.
    //   Here we'll use a ChLinkMateGeneric, but we could also use ChLinkLockSpherical

    auto sphericalLink =
        std::make_shared<ChLinkMateGeneric>(true, true, true, false, false, false);  // x,y,z,Rx,Ry,Rz constrains
    ChFrame<> link_position_abs(ChVector<>(0, 4, 0));

    sphericalLink->Initialize(pendulumBody,        // the 1st body to connect
                              floorBody,           // the 2nd body to connect
                              false,               // the two following frames are in absolute, not relative, coords.
                              link_position_abs,   // the link reference attached to 1st body
                              link_position_abs);  // the link reference attached to 2nd body

    mphysicalSystem.Add(sphericalLink);

    // Optionally, attach a RGB color asset to the floor, for better visualization
    auto color = std::make_shared<ChColorAsset>();
    color->SetColor(ChColor(0.2f, 0.25f, 0.25f));
    floorBody->AddAsset(color);

    // Optionally, attach a texture to the pendulum, for better visualization
    auto texture = std::make_shared<ChTexture>();
    texture->SetTextureFilename(GetChronoDataFile("cubetexture_bluwhite.png"));  // texture in ../data
    pendulumBody->AddAsset(texture);

    //======================================================================

    // Use this function for adding a ChIrrNodeAsset to all items
    // Otherwise use application.AssetBind(myitem); on a per-item basis.
    application.AssetBindAll();

    // Use this function for 'converting' assets into Irrlicht meshes
    application.AssetUpdateAll();

    // Adjust some settings:
    application.SetTimestep(0.005);
    application.SetTryRealtime(true);

    //
    // THE SOFT-REAL-TIME CYCLE
    //

    while (application.GetDevice()->run()) {
        application.BeginScene();

        application.DrawAll();

        // This performs the integration timestep!
        application.DoStep();

        application.EndScene();
    }

    return 0;
}
<commit_msg>Update the sample external project to reflect latest Chrono API changes<commit_after>//
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2013 Project Chrono
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file at the top level of the distribution
// and at http://projectchrono.org/license-chrono.txt.
//

// A very simple example that can be used as template project for
// a Chrono::Engine simulator with 3D view.

#include "chrono/physics/ChSystemNSC.h"
#include "chrono/physics/ChBodyEasy.h"
#include "chrono/physics/ChLinkMate.h"
#include "chrono/assets/ChTexture.h"
#include "chrono/assets/ChColorAsset.h"
#include "chrono_irrlicht/ChIrrApp.h"

// Use the namespace of Chrono

using namespace chrono;
using namespace chrono::irrlicht;

// Use the main namespaces of Irrlicht

using namespace irr;
using namespace irr::core;
using namespace irr::scene;
using namespace irr::video;
using namespace irr::io;
using namespace irr::gui;

int main(int argc, char* argv[]) {
    // Set path to Chrono data directory
    SetChronoDataPath(CHRONO_DATA_DIR);

    // Create a Chrono physical system
    ChSystemNSC mphysicalSystem;

    // Create the Irrlicht visualization (open the Irrlicht device,
    // bind a simple user interface, etc. etc.)
    ChIrrApp application(&mphysicalSystem, L"A simple project template", core::dimension2d<u32>(800, 600),
                         false);  // screen dimensions

    // Easy shortcuts to add camera, lights, logo and sky in Irrlicht scene:
    application.AddTypicalLogo();
    application.AddTypicalSky();
    application.AddTypicalLights();
    application.AddTypicalCamera(core::vector3df(2, 2, -5),
                                 core::vector3df(0, 1, 0));  // to change the position of camera
    // application.AddLightWithShadow(vector3df(1,25,-5), vector3df(0,0,0), 35, 0.2,35, 55, 512, video::SColorf(1,1,1));

    //======================================================================

    // HERE YOU CAN POPULATE THE PHYSICAL SYSTEM WITH BODIES AND LINKS.
    //
    // An example: a pendulum.

    // 1-Create a floor that is fixed (that is used also to represent the absolute reference)

    auto floorBody = std::make_shared<ChBodyEasyBox>(10, 2, 10,  // x, y, z dimensions
                                                     3000,       // density
                                                     false,      // no contact geometry
                                                     true        // enable visualization geometry
                                                     );
    floorBody->SetPos(ChVector<>(0, -2, 0));
    floorBody->SetBodyFixed(true);

    mphysicalSystem.Add(floorBody);

    // 2-Create a pendulum

    auto pendulumBody = std::make_shared<ChBodyEasyBox>(0.5, 2, 0.5,  // x, y, z dimensions
                                                        3000,         // density
                                                        false,        // no contact geometry
                                                        true          // enable visualization geometry
                                                        );
    pendulumBody->SetPos(ChVector<>(0, 3, 0));
    pendulumBody->SetPos_dt(ChVector<>(1, 0, 0));

    mphysicalSystem.Add(pendulumBody);

    // 3-Create a spherical constraint.
    //   Here we'll use a ChLinkMateGeneric, but we could also use ChLinkLockSpherical

    auto sphericalLink =
        std::make_shared<ChLinkMateGeneric>(true, true, true, false, false, false);  // x,y,z,Rx,Ry,Rz constrains
    ChFrame<> link_position_abs(ChVector<>(0, 4, 0));

    sphericalLink->Initialize(pendulumBody,        // the 1st body to connect
                              floorBody,           // the 2nd body to connect
                              false,               // the two following frames are in absolute, not relative, coords.
                              link_position_abs,   // the link reference attached to 1st body
                              link_position_abs);  // the link reference attached to 2nd body

    mphysicalSystem.Add(sphericalLink);

    // Optionally, attach a RGB color asset to the floor, for better visualization
    auto color = std::make_shared<ChColorAsset>();
    color->SetColor(ChColor(0.2f, 0.25f, 0.25f));
    floorBody->AddAsset(color);

    // Optionally, attach a texture to the pendulum, for better visualization
    auto texture = std::make_shared<ChTexture>();
    texture->SetTextureFilename(GetChronoDataFile("cubetexture_bluwhite.png"));  // texture in ../data
    pendulumBody->AddAsset(texture);

    //======================================================================

    // Use this function for adding a ChIrrNodeAsset to all items
    // Otherwise use application.AssetBind(myitem); on a per-item basis.
    application.AssetBindAll();

    // Use this function for 'converting' assets into Irrlicht meshes
    application.AssetUpdateAll();

    // Adjust some settings:
    application.SetTimestep(0.005);
    application.SetTryRealtime(true);

    //
    // THE SOFT-REAL-TIME CYCLE
    //

    while (application.GetDevice()->run()) {
        application.BeginScene();

        application.DrawAll();

        // This performs the integration timestep!
        application.DoStep();

        application.EndScene();
    }

    return 0;
}
<|endoftext|>