trying stuff

trying stuff

// Copyright Hugh Perkins 2014, 2015 hughperkins at gmail
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

// expected defines:
// one of: [ TANH | RELU | LINEAR ]
// BIASED (or not)

#ifdef TANH
    #define ACTIVATION_FUNCTION(output) (tanh(output))
#elif defined SCALEDTANH
    #define ACTIVATION_FUNCTION(output) ( 1.7159f * tanh( 0.66667f * output))
#elif SIGMOID
    #define ACTIVATION_FUNCTION(output) (1.0f / (1 + exp(-output)))
#elif defined RELU
    #define ACTIVATION_FUNCTION(output) (output> 0 ? output : 0)
#elif defined LINEAR
    #define ACTIVATION_FUNCTION(output) (output)
#endif

#ifdef gOutputBoardSize // for previous tests that dont define it
#ifdef ACTIVATION_FUNCTION // protect against not defined
// workgroup id organized like: [imageid][outplane]
// local id organized like: [outrow][outcol]
// each thread iterates over: [upstreamplane][filterrow][filtercol]
// number workgroups = 32
// one filter plane takes up 5 * 5 * 4 = 100 bytes
// one filter cube (corresponding to one outplane) = 5*5 * 32 * 4 = 3.2KB (ok)
// all filter cubes = 3.2KB * 32 = 102KB (too big)
// results are organized like [imageid][filterid][row][col]
void kernel propagate_4_by_n_outplane_smallercache( const int batchSize,
    const int pixelsPerThread,
      global const float *images, global const float *filters,
        #ifdef BIASED
            global const float*biases,
        #endif
    global float *results,
    local float *_upstreamBoard, local float *_filterCube, local float *_pixelSums ) {
    const int globalId = get_global_id(0);

    const int evenPadding = gFilterSize % 2 == 0 ? 1 : 0;

    const int localId = get_local_id(0);
    const int workgroupId = get_group_id(0);
    const int workgroupSize = get_local_size(0);
    const int n = workgroupId / gNumFilters;
    const int outPlane = workgroupId % gNumFilters;

    const int numUpstreamsPerThread = ( gInputBoardSizeSquared + workgroupSize - 1 ) / workgroupSize;
    const int numFilterPixelsPerThread = ( gFilterSizeSquared + workgroupSize - 1 ) / workgroupSize;

    //local float *_myPixelSums = _pixelSums + pixelsPerThread * localId;
    local float *_myPixelSums = _pixelSums;

    for( int pixel = 0; pixel < pixelsPerThread; pixel++ ) {
        _myPixelSums[pixel] = 0.0f;
    }

    const int outputRow = localId / gOutputBoardSize;
    const int outputCol = localId % gOutputBoardSize;

    const int minu = gPadZeros ? max( -gHalfFilterSize, -outputRow ) : -gHalfFilterSize;
    const int maxu = gPadZeros ? min( gHalfFilterSize - evenPadding, gOutputBoardSize - 1 - outputRow  - evenPadding) : gHalfFilterSize - evenPadding;
    const int minv = gPadZeros ? max( -gHalfFilterSize, -outputCol ) : - gHalfFilterSize;
    const int maxv = gPadZeros ? min( gHalfFilterSize - evenPadding, gOutputBoardSize - 1 - outputCol - evenPadding) : gHalfFilterSize - evenPadding;

    for( int upstreamPlane = 0; upstreamPlane < gInputPlanes; upstreamPlane++ ) {
        int thisUpstreamBoardOffset = ( n * gInputPlanes + upstreamPlane ) * gInputBoardSizeSquared;
        barrier(CLK_LOCAL_MEM_FENCE);
        for( int i = 0; i < numUpstreamsPerThread; i++ ) {
            int thisOffset = workgroupSize * i + localId;
            if( thisOffset < gInputBoardSizeSquared ) {
                _upstreamBoard[ thisOffset ] = images[ thisUpstreamBoardOffset + thisOffset ];
            }
        }
        const int filterGlobalOffset = ( outPlane * gInputPlanes + upstreamPlane ) * gFilterSizeSquared;
        for( int i = 0; i < numFilterPixelsPerThread; i++ ) {
            int thisOffset = workgroupSize * i + localId;
            if( thisOffset < gFilterSizeSquared ) {
                _filterCube[thisOffset] = filters[filterGlobalOffset + thisOffset];
            }
        }
        barrier(CLK_LOCAL_MEM_FENCE);

  //      for( int pixel = 0; pixel < pixelsPerThread; pixel++ ) {
        int pixel = 0;
        while( pixel < pixelsPerThread ) {
            const int virtualLocalId = localId + pixel * workgroupSize;

            float thissum = 0;
            if( virtualLocalId < gOutputBoardSizeSquared ) {
                for( int u = minu; u <= maxu; u++ ) {
                    int inputRow = outputRow + u + ( gPadZeros ? 0 : gHalfFilterSize );
                    int inputboardrowoffset = inputRow * gInputBoardSize;
                    int filterrowoffset = (u+gHalfFilterSize) * gFilterSize + gHalfFilterSize;
                    for( int v = minv; v <= maxv; v++ ) {
                        int inputCol = outputCol + v + ( gPadZeros ? 0 : gHalfFilterSize );
                        thissum += _upstreamBoard[ inputboardrowoffset + inputCol] * _filterCube[ filterrowoffset + v ];
                    }
                }
            }
            _myPixelSums[pixel] += thissum;
            pixel++;
//            if( globalId == 0 ) results[0] = _upstreamBoard[pixel];
        }
    }
//    for( int pixel = 0; pixel < pixelsPerThread; pixel++ ) {
        int pixel = 0;
        const int virtualLocalId = localId + pixel * workgroupSize;
        if( virtualLocalId < gOutputBoardSizeSquared ) {
            float sum = _myPixelSums[pixel];
            #ifdef BIASED
                sum += biases[outPlane];
            #endif
            // results are organized like [imageid][filterid][row][col]
            int resultIndex = ( n * gNumFilters + outPlane ) * gOutputBoardSizeSquared + virtualLocalId;
            results[resultIndex ] = ACTIVATION_FUNCTION(sum);
            // results[resultIndex ] = 123;
            //if( globalId == 0 ) results[0] += 0.000001f + _perPixelSums[0];
        }
//    }
}
#endif
#endif

// Copyright Hugh Perkins 2014, 2015 hughperkins at gmail
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

// expected defines:
// one of: [ TANH | RELU | LINEAR ]
// BIASED (or not)

#ifdef TANH
    #define ACTIVATION_FUNCTION(output) (tanh(output))
#elif defined SCALEDTANH
    #define ACTIVATION_FUNCTION(output) ( 1.7159f * tanh( 0.66667f * output))
#elif SIGMOID
    #define ACTIVATION_FUNCTION(output) (1.0f / (1 + exp(-output)))
#elif defined RELU
    #define ACTIVATION_FUNCTION(output) (output> 0 ? output : 0)
#elif defined LINEAR
    #define ACTIVATION_FUNCTION(output) (output)
#endif

#ifdef gOutputBoardSize // for previous tests that dont define it
#ifdef ACTIVATION_FUNCTION // protect against not defined
// workgroup id organized like: [imageid][outplane]
// local id organized like: [outrow][outcol]
// each thread iterates over: [upstreamplane][filterrow][filtercol]
// number workgroups = 32
// one filter plane takes up 5 * 5 * 4 = 100 bytes
// one filter cube (corresponding to one outplane) = 5*5 * 32 * 4 = 3.2KB (ok)
// all filter cubes = 3.2KB * 32 = 102KB (too big)
// results are organized like [imageid][filterid][row][col]
void kernel propagate_4_by_n_outplane_smallercache( const int batchSize,
    const int pixelsPerThread,
      global const float *images, global const float *filters,
        #ifdef BIASED
            global const float*biases,
        #endif
    global float *results,
    local float *_upstreamBoard, local float *_filterCube, local float *_pixelSums ) {
    const int globalId = get_global_id(0);

    const int evenPadding = gFilterSize % 2 == 0 ? 1 : 0;

    const int localId = get_local_id(0);
    const int workgroupId = get_group_id(0);
    const int workgroupSize = get_local_size(0);
    const int n = workgroupId / gNumFilters;
    const int outPlane = workgroupId % gNumFilters;

    const int numUpstreamsPerThread = ( gInputBoardSizeSquared + workgroupSize - 1 ) / workgroupSize;
    const int numFilterPixelsPerThread = ( gFilterSizeSquared + workgroupSize - 1 ) / workgroupSize;

    //local float *_myPixelSums = _pixelSums + pixelsPerThread * localId;
    local float *_myPixelSums = _pixelSums;

    for( int pixel = 0; pixel < pixelsPerThread; pixel++ ) {
        _myPixelSums[pixel] = 0.0f;
    }

    const int outputRow = localId / gOutputBoardSize;
    const int outputCol = localId % gOutputBoardSize;

    const int minu = gPadZeros ? max( -gHalfFilterSize, -outputRow ) : -gHalfFilterSize;
    const int maxu = gPadZeros ? min( gHalfFilterSize - evenPadding, gOutputBoardSize - 1 - outputRow  - evenPadding) : gHalfFilterSize - evenPadding;
    const int minv = gPadZeros ? max( -gHalfFilterSize, -outputCol ) : - gHalfFilterSize;
    const int maxv = gPadZeros ? min( gHalfFilterSize - evenPadding, gOutputBoardSize - 1 - outputCol - evenPadding) : gHalfFilterSize - evenPadding;

    for( int upstreamPlane = 0; upstreamPlane < gInputPlanes; upstreamPlane++ ) {
        int thisUpstreamBoardOffset = ( n * gInputPlanes + upstreamPlane ) * gInputBoardSizeSquared;
        barrier(CLK_LOCAL_MEM_FENCE);
        for( int i = 0; i < numUpstreamsPerThread; i++ ) {
            int thisOffset = workgroupSize * i + localId;
            if( thisOffset < gInputBoardSizeSquared ) {
                _upstreamBoard[ thisOffset ] = images[ thisUpstreamBoardOffset + thisOffset ];
            }
        }
        const int filterGlobalOffset = ( outPlane * gInputPlanes + upstreamPlane ) * gFilterSizeSquared;
        for( int i = 0; i < numFilterPixelsPerThread; i++ ) {
            int thisOffset = workgroupSize * i + localId;
            if( thisOffset < gFilterSizeSquared ) {
                _filterCube[thisOffset] = filters[filterGlobalOffset + thisOffset];
            }
        }
        barrier(CLK_LOCAL_MEM_FENCE);

        for( int pixel = 0; pixel < pixelsPerThread; pixel++ ) {
//        int pixel = 0;
            const int virtualLocalId = localId + pixel * workgroupSize;

            float thissum = 0;
            if( virtualLocalId < gOutputBoardSizeSquared ) {
                for( int u = minu; u <= maxu; u++ ) {
                    int inputRow = outputRow + u + ( gPadZeros ? 0 : gHalfFilterSize );
                    int inputboardrowoffset = inputRow * gInputBoardSize;
                    int filterrowoffset = (u+gHalfFilterSize) * gFilterSize + gHalfFilterSize;
                    for( int v = minv; v <= maxv; v++ ) {
                        int inputCol = outputCol + v + ( gPadZeros ? 0 : gHalfFilterSize );
                        thissum += _upstreamBoard[ inputboardrowoffset + inputCol] * _filterCube[ filterrowoffset + v ];
                    }
                }
            }
            _myPixelSums[pixel] += thissum;
//            if( globalId == 0 ) results[0] = _upstreamBoard[pixel];
        }
    }
//    for( int pixel = 0; pixel < pixelsPerThread; pixel++ ) {
        int pixel = 0;
        const int virtualLocalId = localId + pixel * workgroupSize;
        if( virtualLocalId < gOutputBoardSizeSquared ) {
            float sum = _myPixelSums[pixel];
            #ifdef BIASED
                sum += biases[outPlane];
            #endif
            // results are organized like [imageid][filterid][row][col]
            int resultIndex = ( n * gNumFilters + outPlane ) * gOutputBoardSizeSquared + virtualLocalId;
            results[resultIndex ] = ACTIVATION_FUNCTION(sum);
            // results[resultIndex ] = 123;
            //if( globalId == 0 ) results[0] += 0.000001f + _perPixelSums[0];
        }
//    }
}
#endif
#endif


[Index] Update tests allowing double4 type to be "invalid"

Fixes test failure after r341309

git-svn-id: ffe668792ed300d6c2daa1f6eba2e0aa28d7ec6c@341314 91177308-0d34-0410-b5e6-96231b3b80d8

// RUN: c-index-test -test-print-type %s -cl-std=CL2.0 | FileCheck %s

#pragma OPENCL EXTENSION cl_khr_fp16 : enable
#pragma OPENCL EXTENSION cl_khr_fp64 : enable

typedef half half4 __attribute__((ext_vector_type(4)));
typedef float float4 __attribute__((ext_vector_type(4)));
typedef double double4 __attribute__((ext_vector_type(4)));

void kernel testFloatTypes() {
  half scalarHalf;
  half4 vectorHalf;
  float scalarFloat;
  float4 vectorFloat;
  double scalarDouble;
  double4 vectorDouble;
}

// CHECK: VarDecl=scalarHalf:11:8 (Definition){{( \(invalid\))?}} [type=half] [typekind=Half] [isPOD=1]
// CHECK: VarDecl=vectorHalf:12:9 (Definition) [type=half4] [typekind=Typedef] [canonicaltype=half __attribute__((ext_vector_type(4)))] [canonicaltypekind=Unexposed] [isPOD=1]
// CHECK: VarDecl=scalarFloat:13:9 (Definition) [type=float] [typekind=Float] [isPOD=1]
// CHECK: VarDecl=vectorFloat:14:10 (Definition) [type=float4] [typekind=Typedef] [canonicaltype=float __attribute__((ext_vector_type(4)))] [canonicaltypekind=Unexposed] [isPOD=1]
// CHECK: VarDecl=scalarDouble:15:10 (Definition){{( \(invalid\))?}} [type=double] [typekind=Double] [isPOD=1]
// CHECK: VarDecl=vectorDouble:16:11 (Definition){{( \(invalid\))?}} [type=double4] [typekind=Typedef] [canonicaltype=double __attribute__((ext_vector_type(4)))] [canonicaltypekind=Unexposed] [isPOD=1]

#pragma OPENCL EXTENSION cl_khr_gl_msaa_sharing : enable

void kernel OCLImage1dROTest(read_only image1d_t scalarOCLImage1dRO);
void kernel OCLImage1dArrayROTest(read_only image1d_array_t scalarOCLImage1dArrayRO);
void kernel OCLImage1dBufferROTest(read_only image1d_buffer_t scalarOCLImage1dBufferRO);
void kernel OCLImage2dROTest(read_only image2d_t scalarOCLImage2dRO);
void kernel OCLImage2dArrayROTest(read_only image2d_array_t scalarOCLImage2dArrayRO);
void kernel OCLImage2dDepthROTest(read_only image2d_depth_t scalarOCLImage2dDepthRO);
void kernel OCLImage2dArrayDepthROTest(read_only image2d_array_depth_t scalarOCLImage2dArrayDepthRO);
void kernel OCLImage2dMSAAROTest(read_only image2d_msaa_t scalarOCLImage2dMSAARO);
void kernel OCLImage2dArrayMSAAROTest(read_only image2d_array_msaa_t scalarOCLImage2dArrayMSAARO);
void kernel OCLImage2dMSAADepthROTest(read_only image2d_msaa_depth_t scalarOCLImage2dMSAADepthRO);
void kernel OCLImage2dArrayMSAADepthROTest(read_only image2d_array_msaa_depth_t scalarOCLImage2dArrayMSAADepthRO);
void kernel OCLImage3dROTest(read_only image3d_t scalarOCLImage3dRO);

// CHECK: ParmDecl=scalarOCLImage1dRO:28:50 (Definition) [type=__read_only image1d_t] [typekind=OCLImage1dRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dArrayRO:29:61 (Definition) [type=__read_only image1d_array_t] [typekind=OCLImage1dArrayRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dBufferRO:30:63 (Definition) [type=__read_only image1d_buffer_t] [typekind=OCLImage1dBufferRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dRO:31:50 (Definition) [type=__read_only image2d_t] [typekind=OCLImage2dRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayRO:32:61 (Definition) [type=__read_only image2d_array_t] [typekind=OCLImage2dArrayRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dDepthRO:33:61 (Definition) [type=__read_only image2d_depth_t] [typekind=OCLImage2dDepthRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayDepthRO:34:72 (Definition) [type=__read_only image2d_array_depth_t] [typekind=OCLImage2dArrayDepthRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAARO:35:59 (Definition){{( \(invalid\))?}} [type=__read_only image2d_msaa_t] [typekind=OCLImage2dMSAARO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAARO:36:70 (Definition){{( \(invalid\))?}} [type=__read_only image2d_array_msaa_t] [typekind=OCLImage2dArrayMSAARO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAADepthRO:37:70 (Definition){{( \(invalid\))?}} [type=__read_only image2d_msaa_depth_t] [typekind=OCLImage2dMSAADepthRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAADepthRO:38:81 (Definition){{( \(invalid\))?}} [type=__read_only image2d_array_msaa_depth_t] [typekind=OCLImage2dArrayMSAADepthRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage3dRO:39:50 (Definition) [type=__read_only image3d_t] [typekind=OCLImage3dRO] [isPOD=1]

void kernel OCLImage1dWOTest(write_only image1d_t scalarOCLImage1dWO);
void kernel OCLImage1dArrayWOTest(write_only image1d_array_t scalarOCLImage1dArrayWO);
void kernel OCLImage1dBufferWOTest(write_only image1d_buffer_t scalarOCLImage1dBufferWO);
void kernel OCLImage2dWOTest(write_only image2d_t scalarOCLImage2dWO);
void kernel OCLImage2dArrayWOTest(write_only image2d_array_t scalarOCLImage2dArrayWO);
void kernel OCLImage2dDepthWOTest(write_only image2d_depth_t scalarOCLImage2dDepthWO);
void kernel OCLImage2dArrayDepthWOTest(write_only image2d_array_depth_t scalarOCLImage2dArrayDepthWO);
void kernel OCLImage2dMSAAWOTest(write_only image2d_msaa_t scalarOCLImage2dMSAAWO);
void kernel OCLImage2dArrayMSAAWOTest(write_only image2d_array_msaa_t scalarOCLImage2dArrayMSAAWO);
void kernel OCLImage2dMSAADepthWOTest(write_only image2d_msaa_depth_t scalarOCLImage2dMSAADepthWO);
void kernel OCLImage2dArrayMSAADepthWOTest(write_only image2d_array_msaa_depth_t scalarOCLImage2dArrayMSAADepthWO);
void kernel OCLImage3dWOTest(write_only image3d_t scalarOCLImage3dWO);

// CHECK: ParmDecl=scalarOCLImage1dWO:54:51 (Definition) [type=__write_only image1d_t] [typekind=OCLImage1dWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dArrayWO:55:62 (Definition) [type=__write_only image1d_array_t] [typekind=OCLImage1dArrayWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dBufferWO:56:64 (Definition) [type=__write_only image1d_buffer_t] [typekind=OCLImage1dBufferWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dWO:57:51 (Definition) [type=__write_only image2d_t] [typekind=OCLImage2dWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayWO:58:62 (Definition) [type=__write_only image2d_array_t] [typekind=OCLImage2dArrayWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dDepthWO:59:62 (Definition) [type=__write_only image2d_depth_t] [typekind=OCLImage2dDepthWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayDepthWO:60:73 (Definition) [type=__write_only image2d_array_depth_t] [typekind=OCLImage2dArrayDepthWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAAWO:61:60 (Definition){{( \(invalid\))?}} [type=__write_only image2d_msaa_t] [typekind=OCLImage2dMSAAWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAAWO:62:71 (Definition){{( \(invalid\))?}} [type=__write_only image2d_array_msaa_t] [typekind=OCLImage2dArrayMSAAWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAADepthWO:63:71 (Definition){{( \(invalid\))?}} [type=__write_only image2d_msaa_depth_t] [typekind=OCLImage2dMSAADepthWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAADepthWO:64:82 (Definition){{( \(invalid\))?}} [type=__write_only image2d_array_msaa_depth_t] [typekind=OCLImage2dArrayMSAADepthWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage3dWO:65:51 (Definition){{( \(invalid\))?}} [type=__write_only image3d_t] [typekind=OCLImage3dWO] [isPOD=1]

void kernel OCLImage1dRWTest(read_write image1d_t scalarOCLImage1dRW);
void kernel OCLImage1dArrayRWTest(read_write image1d_array_t scalarOCLImage1dArrayRW);
void kernel OCLImage1dBufferRWTest(read_write image1d_buffer_t scalarOCLImage1dBufferRW);
void kernel OCLImage2dRWTest(read_write image2d_t scalarOCLImage2dRW);
void kernel OCLImage2dArrayRWTest(read_write image2d_array_t scalarOCLImage2dArrayRW);
void kernel OCLImage2dDepthRWTest(read_write image2d_depth_t scalarOCLImage2dDepthRW);
void kernel OCLImage2dArrayDepthRWTest(read_write image2d_array_depth_t scalarOCLImage2dArrayDepthRW);
void kernel OCLImage2dMSAARWTest(read_write image2d_msaa_t scalarOCLImage2dMSAARW);
void kernel OCLImage2dArrayMSAARWTest(read_write image2d_array_msaa_t scalarOCLImage2dArrayMSAARW);
void kernel OCLImage2dMSAADepthRWTest(read_write image2d_msaa_depth_t scalarOCLImage2dMSAADepthRW);
void kernel OCLImage2dArrayMSAADepthRWTest(read_write image2d_array_msaa_depth_t scalarOCLImage2dArrayMSAADepthRW);
void kernel OCLImage3dRWTest(read_write image3d_t scalarOCLImage3dRW);

// CHECK: ParmDecl=scalarOCLImage1dRW:80:51 (Definition) [type=__read_write image1d_t] [typekind=OCLImage1dRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dArrayRW:81:62 (Definition) [type=__read_write image1d_array_t] [typekind=OCLImage1dArrayRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dBufferRW:82:64 (Definition) [type=__read_write image1d_buffer_t] [typekind=OCLImage1dBufferRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dRW:83:51 (Definition) [type=__read_write image2d_t] [typekind=OCLImage2dRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayRW:84:62 (Definition) [type=__read_write image2d_array_t] [typekind=OCLImage2dArrayRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dDepthRW:85:62 (Definition) [type=__read_write image2d_depth_t] [typekind=OCLImage2dDepthRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayDepthRW:86:73 (Definition) [type=__read_write image2d_array_depth_t] [typekind=OCLImage2dArrayDepthRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAARW:87:60 (Definition){{( \(invalid\))?}} [type=__read_write image2d_msaa_t] [typekind=OCLImage2dMSAARW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAARW:88:71 (Definition){{( \(invalid\))?}} [type=__read_write image2d_array_msaa_t] [typekind=OCLImage2dArrayMSAARW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAADepthRW:89:71 (Definition){{( \(invalid\))?}} [type=__read_write image2d_msaa_depth_t] [typekind=OCLImage2dMSAADepthRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAADepthRW:90:82 (Definition){{( \(invalid\))?}} [type=__read_write image2d_array_msaa_depth_t] [typekind=OCLImage2dArrayMSAADepthRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage3dRW:91:51 (Definition) [type=__read_write image3d_t] [typekind=OCLImage3dRW] [isPOD=1]

void kernel intPipeTestRO(read_only pipe int scalarPipe);
void kernel intPipeTestWO(write_only pipe int scalarPipe);

// CHECK: ParmDecl=scalarPipe:106:46 (Definition) [type=read_only pipe int] [typekind=Pipe] [isPOD=0]
// CHECK: ParmDecl=scalarPipe:107:47 (Definition) [type=write_only pipe int] [typekind=Pipe] [isPOD=0]

#define CLK_ADDRESS_CLAMP_TO_EDGE       2
#define CLK_NORMALIZED_COORDS_TRUE      1
#define CLK_FILTER_NEAREST              0x10

void kernel testMiscOpenCLTypes() {
  const sampler_t scalarOCLSampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;
  clk_event_t scalarOCLEvent;
  queue_t scalarOCLQueue;
  reserve_id_t scalarOCLReserveID;
}

// CHECK: VarDecl=scalarOCLSampler:117:19 (Definition) [type=const sampler_t] [typekind=Typedef] const [canonicaltype=const sampler_t] [canonicaltypekind=OCLSampler] [isPOD=1]
// CHECK: VarDecl=scalarOCLEvent:118:15 (Definition) [type=clk_event_t] [typekind=Typedef] [canonicaltype=clk_event_t] [canonicaltypekind=Unexposed] [isPOD=1]
// CHECK: VarDecl=scalarOCLQueue:119:11 (Definition) [type=queue_t] [typekind=Typedef] [canonicaltype=queue_t] [canonicaltypekind=OCLQueue] [isPOD=1]
// CHECK: VarDecl=scalarOCLReserveID:120:16 (Definition) [type=reserve_id_t] [typekind=Typedef] [canonicaltype=reserve_id_t] [canonicaltypekind=OCLReserveID] [isPOD=1]

// RUN: c-index-test -test-print-type %s -cl-std=CL2.0 | FileCheck %s

#pragma OPENCL EXTENSION cl_khr_fp16 : enable
#pragma OPENCL EXTENSION cl_khr_fp64 : enable

typedef half half4 __attribute__((ext_vector_type(4)));
typedef float float4 __attribute__((ext_vector_type(4)));
typedef double double4 __attribute__((ext_vector_type(4)));

void kernel testFloatTypes() {
  half scalarHalf;
  half4 vectorHalf;
  float scalarFloat;
  float4 vectorFloat;
  double scalarDouble;
  double4 vectorDouble;
}

// CHECK: VarDecl=scalarHalf:11:8 (Definition){{( \(invalid\))?}} [type=half] [typekind=Half] [isPOD=1]
// CHECK: VarDecl=vectorHalf:12:9 (Definition) [type=half4] [typekind=Typedef] [canonicaltype=half __attribute__((ext_vector_type(4)))] [canonicaltypekind=Unexposed] [isPOD=1]
// CHECK: VarDecl=scalarFloat:13:9 (Definition) [type=float] [typekind=Float] [isPOD=1]
// CHECK: VarDecl=vectorFloat:14:10 (Definition) [type=float4] [typekind=Typedef] [canonicaltype=float __attribute__((ext_vector_type(4)))] [canonicaltypekind=Unexposed] [isPOD=1]
// CHECK: VarDecl=scalarDouble:15:10 (Definition){{( \(invalid\))?}} [type=double] [typekind=Double] [isPOD=1]
// CHECK: VarDecl=vectorDouble:16:11 (Definition) [type=double4] [typekind=Typedef] [canonicaltype=double __attribute__((ext_vector_type(4)))] [canonicaltypekind=Unexposed] [isPOD=1]

#pragma OPENCL EXTENSION cl_khr_gl_msaa_sharing : enable

void kernel OCLImage1dROTest(read_only image1d_t scalarOCLImage1dRO);
void kernel OCLImage1dArrayROTest(read_only image1d_array_t scalarOCLImage1dArrayRO);
void kernel OCLImage1dBufferROTest(read_only image1d_buffer_t scalarOCLImage1dBufferRO);
void kernel OCLImage2dROTest(read_only image2d_t scalarOCLImage2dRO);
void kernel OCLImage2dArrayROTest(read_only image2d_array_t scalarOCLImage2dArrayRO);
void kernel OCLImage2dDepthROTest(read_only image2d_depth_t scalarOCLImage2dDepthRO);
void kernel OCLImage2dArrayDepthROTest(read_only image2d_array_depth_t scalarOCLImage2dArrayDepthRO);
void kernel OCLImage2dMSAAROTest(read_only image2d_msaa_t scalarOCLImage2dMSAARO);
void kernel OCLImage2dArrayMSAAROTest(read_only image2d_array_msaa_t scalarOCLImage2dArrayMSAARO);
void kernel OCLImage2dMSAADepthROTest(read_only image2d_msaa_depth_t scalarOCLImage2dMSAADepthRO);
void kernel OCLImage2dArrayMSAADepthROTest(read_only image2d_array_msaa_depth_t scalarOCLImage2dArrayMSAADepthRO);
void kernel OCLImage3dROTest(read_only image3d_t scalarOCLImage3dRO);

// CHECK: ParmDecl=scalarOCLImage1dRO:28:50 (Definition) [type=__read_only image1d_t] [typekind=OCLImage1dRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dArrayRO:29:61 (Definition) [type=__read_only image1d_array_t] [typekind=OCLImage1dArrayRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dBufferRO:30:63 (Definition) [type=__read_only image1d_buffer_t] [typekind=OCLImage1dBufferRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dRO:31:50 (Definition) [type=__read_only image2d_t] [typekind=OCLImage2dRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayRO:32:61 (Definition) [type=__read_only image2d_array_t] [typekind=OCLImage2dArrayRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dDepthRO:33:61 (Definition) [type=__read_only image2d_depth_t] [typekind=OCLImage2dDepthRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayDepthRO:34:72 (Definition) [type=__read_only image2d_array_depth_t] [typekind=OCLImage2dArrayDepthRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAARO:35:59 (Definition){{( \(invalid\))?}} [type=__read_only image2d_msaa_t] [typekind=OCLImage2dMSAARO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAARO:36:70 (Definition){{( \(invalid\))?}} [type=__read_only image2d_array_msaa_t] [typekind=OCLImage2dArrayMSAARO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAADepthRO:37:70 (Definition){{( \(invalid\))?}} [type=__read_only image2d_msaa_depth_t] [typekind=OCLImage2dMSAADepthRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAADepthRO:38:81 (Definition){{( \(invalid\))?}} [type=__read_only image2d_array_msaa_depth_t] [typekind=OCLImage2dArrayMSAADepthRO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage3dRO:39:50 (Definition) [type=__read_only image3d_t] [typekind=OCLImage3dRO] [isPOD=1]

void kernel OCLImage1dWOTest(write_only image1d_t scalarOCLImage1dWO);
void kernel OCLImage1dArrayWOTest(write_only image1d_array_t scalarOCLImage1dArrayWO);
void kernel OCLImage1dBufferWOTest(write_only image1d_buffer_t scalarOCLImage1dBufferWO);
void kernel OCLImage2dWOTest(write_only image2d_t scalarOCLImage2dWO);
void kernel OCLImage2dArrayWOTest(write_only image2d_array_t scalarOCLImage2dArrayWO);
void kernel OCLImage2dDepthWOTest(write_only image2d_depth_t scalarOCLImage2dDepthWO);
void kernel OCLImage2dArrayDepthWOTest(write_only image2d_array_depth_t scalarOCLImage2dArrayDepthWO);
void kernel OCLImage2dMSAAWOTest(write_only image2d_msaa_t scalarOCLImage2dMSAAWO);
void kernel OCLImage2dArrayMSAAWOTest(write_only image2d_array_msaa_t scalarOCLImage2dArrayMSAAWO);
void kernel OCLImage2dMSAADepthWOTest(write_only image2d_msaa_depth_t scalarOCLImage2dMSAADepthWO);
void kernel OCLImage2dArrayMSAADepthWOTest(write_only image2d_array_msaa_depth_t scalarOCLImage2dArrayMSAADepthWO);
void kernel OCLImage3dWOTest(write_only image3d_t scalarOCLImage3dWO);

// CHECK: ParmDecl=scalarOCLImage1dWO:54:51 (Definition) [type=__write_only image1d_t] [typekind=OCLImage1dWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dArrayWO:55:62 (Definition) [type=__write_only image1d_array_t] [typekind=OCLImage1dArrayWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dBufferWO:56:64 (Definition) [type=__write_only image1d_buffer_t] [typekind=OCLImage1dBufferWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dWO:57:51 (Definition) [type=__write_only image2d_t] [typekind=OCLImage2dWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayWO:58:62 (Definition) [type=__write_only image2d_array_t] [typekind=OCLImage2dArrayWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dDepthWO:59:62 (Definition) [type=__write_only image2d_depth_t] [typekind=OCLImage2dDepthWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayDepthWO:60:73 (Definition) [type=__write_only image2d_array_depth_t] [typekind=OCLImage2dArrayDepthWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAAWO:61:60 (Definition){{( \(invalid\))?}} [type=__write_only image2d_msaa_t] [typekind=OCLImage2dMSAAWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAAWO:62:71 (Definition){{( \(invalid\))?}} [type=__write_only image2d_array_msaa_t] [typekind=OCLImage2dArrayMSAAWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAADepthWO:63:71 (Definition){{( \(invalid\))?}} [type=__write_only image2d_msaa_depth_t] [typekind=OCLImage2dMSAADepthWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAADepthWO:64:82 (Definition){{( \(invalid\))?}} [type=__write_only image2d_array_msaa_depth_t] [typekind=OCLImage2dArrayMSAADepthWO] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage3dWO:65:51 (Definition){{( \(invalid\))?}} [type=__write_only image3d_t] [typekind=OCLImage3dWO] [isPOD=1]

void kernel OCLImage1dRWTest(read_write image1d_t scalarOCLImage1dRW);
void kernel OCLImage1dArrayRWTest(read_write image1d_array_t scalarOCLImage1dArrayRW);
void kernel OCLImage1dBufferRWTest(read_write image1d_buffer_t scalarOCLImage1dBufferRW);
void kernel OCLImage2dRWTest(read_write image2d_t scalarOCLImage2dRW);
void kernel OCLImage2dArrayRWTest(read_write image2d_array_t scalarOCLImage2dArrayRW);
void kernel OCLImage2dDepthRWTest(read_write image2d_depth_t scalarOCLImage2dDepthRW);
void kernel OCLImage2dArrayDepthRWTest(read_write image2d_array_depth_t scalarOCLImage2dArrayDepthRW);
void kernel OCLImage2dMSAARWTest(read_write image2d_msaa_t scalarOCLImage2dMSAARW);
void kernel OCLImage2dArrayMSAARWTest(read_write image2d_array_msaa_t scalarOCLImage2dArrayMSAARW);
void kernel OCLImage2dMSAADepthRWTest(read_write image2d_msaa_depth_t scalarOCLImage2dMSAADepthRW);
void kernel OCLImage2dArrayMSAADepthRWTest(read_write image2d_array_msaa_depth_t scalarOCLImage2dArrayMSAADepthRW);
void kernel OCLImage3dRWTest(read_write image3d_t scalarOCLImage3dRW);

// CHECK: ParmDecl=scalarOCLImage1dRW:80:51 (Definition) [type=__read_write image1d_t] [typekind=OCLImage1dRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dArrayRW:81:62 (Definition) [type=__read_write image1d_array_t] [typekind=OCLImage1dArrayRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage1dBufferRW:82:64 (Definition) [type=__read_write image1d_buffer_t] [typekind=OCLImage1dBufferRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dRW:83:51 (Definition) [type=__read_write image2d_t] [typekind=OCLImage2dRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayRW:84:62 (Definition) [type=__read_write image2d_array_t] [typekind=OCLImage2dArrayRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dDepthRW:85:62 (Definition) [type=__read_write image2d_depth_t] [typekind=OCLImage2dDepthRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayDepthRW:86:73 (Definition) [type=__read_write image2d_array_depth_t] [typekind=OCLImage2dArrayDepthRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAARW:87:60 (Definition){{( \(invalid\))?}} [type=__read_write image2d_msaa_t] [typekind=OCLImage2dMSAARW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAARW:88:71 (Definition){{( \(invalid\))?}} [type=__read_write image2d_array_msaa_t] [typekind=OCLImage2dArrayMSAARW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dMSAADepthRW:89:71 (Definition){{( \(invalid\))?}} [type=__read_write image2d_msaa_depth_t] [typekind=OCLImage2dMSAADepthRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage2dArrayMSAADepthRW:90:82 (Definition){{( \(invalid\))?}} [type=__read_write image2d_array_msaa_depth_t] [typekind=OCLImage2dArrayMSAADepthRW] [isPOD=1]
// CHECK: ParmDecl=scalarOCLImage3dRW:91:51 (Definition) [type=__read_write image3d_t] [typekind=OCLImage3dRW] [isPOD=1]

void kernel intPipeTestRO(read_only pipe int scalarPipe);
void kernel intPipeTestWO(write_only pipe int scalarPipe);

// CHECK: ParmDecl=scalarPipe:106:46 (Definition) [type=read_only pipe int] [typekind=Pipe] [isPOD=0]
// CHECK: ParmDecl=scalarPipe:107:47 (Definition) [type=write_only pipe int] [typekind=Pipe] [isPOD=0]

#define CLK_ADDRESS_CLAMP_TO_EDGE       2
#define CLK_NORMALIZED_COORDS_TRUE      1
#define CLK_FILTER_NEAREST              0x10

void kernel testMiscOpenCLTypes() {
  const sampler_t scalarOCLSampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;
  clk_event_t scalarOCLEvent;
  queue_t scalarOCLQueue;
  reserve_id_t scalarOCLReserveID;
}

// CHECK: VarDecl=scalarOCLSampler:117:19 (Definition) [type=const sampler_t] [typekind=Typedef] const [canonicaltype=const sampler_t] [canonicaltypekind=OCLSampler] [isPOD=1]
// CHECK: VarDecl=scalarOCLEvent:118:15 (Definition) [type=clk_event_t] [typekind=Typedef] [canonicaltype=clk_event_t] [canonicaltypekind=Unexposed] [isPOD=1]
// CHECK: VarDecl=scalarOCLQueue:119:11 (Definition) [type=queue_t] [typekind=Typedef] [canonicaltype=queue_t] [canonicaltypekind=OCLQueue] [isPOD=1]
// CHECK: VarDecl=scalarOCLReserveID:120:16 (Definition) [type=reserve_id_t] [typekind=Typedef] [canonicaltype=reserve_id_t] [canonicaltypekind=OCLReserveID] [isPOD=1]


Cleaned up tracer code so its easier for intel cl compiler to process

// Global parameters that will be set via a template during compilation time.
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
#define FRAME_W 512
#define FRAME_H 512
#define TEXEL_STEP_X 1.0f / 512.0f
#define TEXEL_STEP_Y 1.0f / 512.0f
#define NO_MATERIAL_HIT -1

// Limits
#define MAX_TRACE_STEPS 32
#define MAX_BOUNCES 4 // iris pro compilation runs out of memory if set to > 5
#define MIN_BOUNCES_TO_USE_RR 2

// Epsilon values
#define NUDGE_EPSILON 1.0e-2f
#define DIST_SNAP_EPSILON 1.0e-3f

// Vector helpers
#define VEC3_MAX_COMPONENT(v) max(v.x, max(v.y, v.z))
#define VEC1(x) (float4)(x, 0.0f, 0.0f, 0.0f)
#define VEC2(x,y) (float4)(x, y, 0.0f, 0.0f)
#define VEC3(x,y,z) (float4)(x,y,z,0.0f)
#define VEC4(x,y,z,w) (float4)(x,y,z,w)
#define ORIGIN(v) VEC3(v,v,v)

// Normal estimator generators
#define OBJECT_NORMAL_AT(estimatorFn, objIndex, point) \
	normalize( \
		(float4)( \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(NUDGE_EPSILON, 0,0)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(NUDGE_EPSILON, 0,0)), \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(0, NUDGE_EPSILON,0)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(0, NUDGE_EPSILON,0)), \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(0, 0, NUDGE_EPSILON)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(0, 0, NUDGE_EPSILON)), \
			0.0f \
		) \
	)

// Tracer structs
// ---------------------

typedef struct {
	float4 position;
	float4 normal;
	float distFromOrigin;
	int objIndex;
	int matIndex;
} Hit;

// Supported material types
#define MATERIAL_DIFFUSE 0
#define MATERIAL_SPECULAR 1
#define MATERIAL_REFRACTIVE 2
#define MATERIAL_LIGHT 65535

typedef struct {
	unsigned int type;
	float4 diffuse;
	float4 emissive;
} Material;

// Supported object types
#define OBJECT_PLANE 0
#define OBJECT_SPHERE 1
#define OBJECT_BOX 2
#define OBJECT_TORUS 3

typedef struct {
	unsigned int type;
	unsigned int material;
	float4 origin;
	float4 params;
} Object;

// -----------------------
// Object and material definitions
// The following code will be autogenerated by the scene processor
// ------------------
__constant float4 sceneBgColor = (float4)(0.0f);

__constant Material SceneMaterial[] = {
	{MATERIAL_LIGHT, VEC3(0.0f,0.0f,0.0f), VEC3(60.0f,60.0f,60.0f)},
	{MATERIAL_DIFFUSE, VEC3(0.75f, 0.25f, 0.25f)},
	{MATERIAL_DIFFUSE, VEC3(0.25f, 0.25f, 0.75f)},
	{MATERIAL_DIFFUSE, VEC3(0.75f, 0.75f, 0.75f)},
	{MATERIAL_SPECULAR, VEC3(0.999f, 0.999f, 0.999f)},
	{MATERIAL_REFRACTIVE, VEC3(0.999f, 0.999f, 0.999f)}
};

__constant Object SceneObject[] = {
	// Light
	{OBJECT_SPHERE, 0, VEC3(0.0f, 1.5f, 0.0f), VEC1(0.1f)},
	// Left wall
	{OBJECT_BOX, 1, VEC3(-2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f)},
	// Right wall
	{OBJECT_BOX, 2, VEC3(2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f)},
	// Top wall
	{OBJECT_BOX, 3, VEC3(0.0f,2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f)},
	// Bottom wall
	{OBJECT_BOX, 3, VEC3(0.0f,-2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f)},
	// Back wall
	{OBJECT_BOX, 3, VEC3(0,0,-2), VEC3(2.0f, 2.0f, 0.1f)},
	// Mirror ball
	{OBJECT_SPHERE, 4, VEC3(-1.0f, -1.4f, -1.0f), VEC1(0.5f)},
	// Glass ball
	{OBJECT_SPHERE, 5, VEC3(1.4f, -1.4f, -0.5f), VEC1(0.5f)}
};

// -----------------------
// Random number and direction generators
// -----------------------
float2 clRand(uint2 *state)
{
    const float2 invMaxInt = (float2) (1.0f/4294967296.0f, 1.0f/4294967296.0f);
    uint x = (*state).x * 17 + (*state).y * 13123;
    (*state).x = (x<<13) ^ x;
    (*state).y ^= (x<<7);

    uint2 tmp = (uint2)
    (
		(x * (x * x * 15731 + 74323) + 871483),
		(x * (x * x * 13734 + 37828) + 234234)
	);

    return convert_float2(tmp) * invMaxInt;
}

// Return random direction on hemiphere around normal.
// This function has: PDF = cos(theta) / pi
float4 rndCosWeightedHemisphereDir(float4 normal, uint2 *seed) {
	float2 rnd  = clRand(seed);

	// Generate point on disk
	float rd = sqrt(rnd.x);
	float phi = 2.0f*M_PI*rnd.y;

	// Generate tangent, bi-tangent vectors
	float4 u = normalize(cross((fabs(normal.x) > .1f ? (float4)(0.0f, 1.0f, 0.0f, 0.0f) : (float4)(1.0f, 0.0f, 0.0f, 0.0f)), normal));
	float4 v = cross(normal,u);

	// Project disk point to unit hemisphere and rotate so that the normal points up
	return normalize(u * rd * cos(phi) + v * rd * sin(phi) + normal * sqrt(1 - rnd.x));
}

// -----------------------
// Distance estimators for supported primitives
// -----------------------
float planeSD(float4 origin, float4 dims, float4 point){
	dims = normalize(dims);
	return dot((point - origin).xyz, dims.xyz) + dims.w;
}
float sphereSD(float4 origin, float4 radius, float4 point){
	return length(point - origin) - radius.x;
}
float boxSD(float4 origin, float4 dims, float4 point){
	float3 d = fabs(point.xyz - origin.xyz) - dims.xyz;
	return fmin(VEC3_MAX_COMPONENT(d), 0.0f) + length(fmax(d, 0.0f));
}
float torusSD(float4 origin, float4 dims, float4 point){
	point -= origin;
	return length((float2)(length(point.xz) - dims.x, point.y)) - dims.y;
}

// -----------------------
// Tracer implementation
// -----------------------

// Estimate normal at intersection point
void getNormal(Hit *hit){
	switch(SceneObject[hit->objIndex].type) {
		case OBJECT_PLANE:
			hit->normal = OBJECT_NORMAL_AT(planeSD, hit->objIndex, hit->position);
		break;
		case OBJECT_SPHERE:
			hit->normal = OBJECT_NORMAL_AT(sphereSD, hit->objIndex, hit->position);
		break;
		case OBJECT_BOX:
			hit->normal = OBJECT_NORMAL_AT(boxSD, hit->objIndex, hit->position);
		break;
		case OBJECT_TORUS:
			hit->normal = OBJECT_NORMAL_AT(torusSD, hit->objIndex, hit->position);
		break;
	}
}

// Estimate distance to nearest object
void intersectWorld(const float4 rayOrigin, const float4 rayDir, const float minDist, const float maxDist, Hit *hit){
	hit->objIndex = NO_MATERIAL_HIT;
	hit->matIndex = NO_MATERIAL_HIT;

	float4 point;
	float curDist = minDist;
	float objDist, nearestDist;
	int nearestObj = -1;
	int nearestMat = -1;
	unsigned int objIndex;
	unsigned int numObjects = (uint)(sizeof(SceneObject) / sizeof(Object));
	for(unsigned int step=0;step<MAX_TRACE_STEPS;step++){
		point = rayOrigin + rayDir * curDist;

		// Find nearest object/dist to point
		nearestDist = FLT_MAX;
		for(objIndex=0;objIndex<numObjects; objIndex++){
			switch(SceneObject[objIndex].type) {
				case OBJECT_PLANE:
					objDist = planeSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_SPHERE:
					objDist = sphereSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_BOX:
					objDist = boxSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_TORUS:
					objDist = torusSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
			}

			if(objDist < nearestDist){
				nearestDist = objDist;
				nearestObj = objIndex;
				nearestMat = SceneObject[objIndex].material;
			}
		}

		// If we are inside an object nearestDist will be < 0
		nearestDist = fabs(nearestDist);

		curDist += nearestDist;
		if(curDist > maxDist){
			return;
		}

		// We are close enough to the object to register a hit
		if(nearestDist < DIST_SNAP_EPSILON){
			hit->position = point;
			hit->distFromOrigin = curDist;
			hit->objIndex = nearestObj;
			hit->matIndex = nearestMat;
			return;
		}
	}
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir, uint2 *rndSeed){
	Hit hit;
	__constant Material *material;
	float4 rCol = (float4)(0.0f);
	float4 mask = (float4)(1.0f);
	unsigned int bounce = 0;
	for(bounce=0;bounce<MAX_BOUNCES;bounce++){
		intersectWorld(rayOrigin, rayDir, NUDGE_EPSILON, FLT_MAX, &hit);

		// No hit
		if(hit.objIndex == NO_MATERIAL_HIT){
			if(bounce == 0){
				rCol = sceneBgColor;
			}
			break;
		}

		// Get material at hit point
		material = &SceneMaterial[hit.matIndex];

		// If we hit a light just add its emissive property and stop
		if( material->type == MATERIAL_LIGHT ){
			rCol += mask * material->emissive;
			break;
		}

		// Get normal at hit point
		getNormal(&hit);

		// After some bounces apply a russian roulette to terminate long paths
		if( bounce > MIN_BOUNCES_TO_USE_RR ) {
			float maxComponent = VEC3_MAX_COMPONENT(material->diffuse);
			if( maxComponent == 0 || clRand(rndSeed).x > maxComponent ){
				break;
			}

			// Boost surviving paths by 1/rr_prob
			mask /= maxComponent;
		}

		// Mask outgoing reflectance by material diffuse property
		mask *= material->diffuse;

		// Next bounce starts at hit point
		rayOrigin = hit.position;

		if( material->type == MATERIAL_DIFFUSE ) {
			// We do importance sampling for diffuse rays
			rayDir = rndCosWeightedHemisphereDir(hit.normal, rndSeed);
		} else{
			// Reflect ray around normal
			rayDir = normalize(-2.0f * dot(hit.normal, rayDir) * hit.normal + rayDir);
		}

		++bounce;
	}

	return rCol;
}

// Emit the color of a pixel by tracing a ray through the scene.
__kernel void tracePixel(
		__global float4 *frameBuffer,
		__global float4 *frustrumCorners,
		const float4 eyePos,
		const unsigned int blockY,
		const unsigned int samplesPerPixel,
		const float exposure,
		const int seed
		){
	// Get pixel coordinates
	unsigned int x = get_global_id(0);
	unsigned int y = get_global_id(1);
	if ( x > FRAME_W || y > FRAME_H ) {
		return;
	}

	// Setup seed for random numbers
	uint2 rndSeed = (uint2)(x * seed, y * seed);

	// Calculate texel coordinates [0,1] range
	float accumScaler = 1.0f / (float)samplesPerPixel;
	float4 accum = (float4)(0.0f,0.0f,0.0f,0.0f);

	for( uint sample=0;sample<samplesPerPixel;sample++){
		// Apply stratified sampling using a tent filter. This will wrap our
		// random numbers in the [-1, 1] range. X and Y point to the top corner
		// of the current texel so we need to add a bit of offset to get the coords
		// into the [-0.5, 1.5] range.
		float2 random = clRand(&rndSeed);
		float offX = random.x < 0.5f ? sqrt(2.0f * random.x) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.x);
		float offY = random.y < 0.5f ? sqrt(2.0f * random.y) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.y);
		float tx = ((float)x + offX) * TEXEL_STEP_X;
		float ty = ((float)y + offY) * TEXEL_STEP_Y;

		// Get ray direction using trilinear interpolation; trace ray and
		// add result to accumulation buffer
		float4 lVec = frustrumCorners[0] * (1 - ty) + frustrumCorners[2] * ty;
		float4 rVec = frustrumCorners[1] * (1 - ty) + frustrumCorners[3] * ty;
		accum += traceRay(eyePos, normalize(lVec * (1 - tx) + rVec * tx), &rndSeed);
	}

	// Average samples
	accum *= accumScaler;

	// Apply tone-mapping and gamma correction using:
	// 1 - exp(-hdrColor * exposure)) [tone mapping HDR -> LDR]
	// pow(ldr, 0.45)) [gamma correction]
	frameBuffer[y*FRAME_W + x] = pow(-expm1(-accum*exposure), 0.45f);
}

// Global parameters that will be set via a template during compilation time.
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
#define FRAME_W 512
#define FRAME_H 512
#define TEXEL_STEP_X 1.0f / 512.0f
#define TEXEL_STEP_Y 1.0f / 512.0f
#define NO_SURFACE_HIT -1

// Limits
#define MAX_TRACE_STEPS 32
#define MAX_BOUNCES 4 // iris pro compilation runs out of memory if set to > 5
#define MIN_BOUNCES_TO_USE_RR 3

// Epsilon values
#define NUDGE_EPSILON 1.0e-2f
#define DIST_SNAP_EPSILON 1.0e-3f

// Vector helpers
#define VEC3_MAX_COMPONENT(v) max(v.x, max(v.y, v.z))
#define VEC3(x,y,z) (float4)(x,y,z,0.0f)
#define VEC3_ORIGIN VEC3(0,0,0)

// Supported surface types
#define SURFACE_DIFFUSE 0
#define SURFACE_SPECULAR 1
#define SURFACE_REFRACTIVE 2
#define SURFACE_LIGHT 65535

// DEBUGGING
#define DEBUG (get_global_id(0)==256 && get_global_id(1)==256)

// Distance estimator function generators
// --------------------------------------
#define PLANE_SD_FN(objIndex, origin, planeDims) float distToObj##objIndex( float4 point ); \
float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = normalize(planeDims); \
	return dot(point, dims); \
}
#define SPHERE_SD_FN(objIndex, origin, radius) float distToObj##objIndex( float4 point ); \
float distToObj##objIndex( float4 point ){ \
	return length(point - origin) - radius; \
}
#define BOX_SD_FN(objIndex, origin, boxDims) float distToObj##objIndex( float4 point ); \
float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = boxDims; \
	float4 d = fabs(point) - (float4)(dims.xyz, point.w); \
	return fmin(VEC3_MAX_COMPONENT(d), 0.0f) + length(fmax(d, 0.0f)); \
}
#define TORUS_SD_FN(objIndex, origin, torusDims) float distToObj##objIndex( float4 point ) \
float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float2 dims = torusDims; \
	return length((float2)(length(point.xz) - dims.x, point.y)) - dims.y; \
}

// Normal estimator generators
#define OBJECT_NORMAL_AT(objIndex, point) \
	normalize( \
		(float4)( \
			distToObj##objIndex(point + VEC3(NUDGE_EPSILON, 0,0)) - distToObj##objIndex(point - VEC3(NUDGE_EPSILON, 0,0)), \
			distToObj##objIndex(point + VEC3(0, NUDGE_EPSILON,0)) - distToObj##objIndex(point - VEC3(0, NUDGE_EPSILON,0)), \
			distToObj##objIndex(point + VEC3(0, 0, NUDGE_EPSILON)) - distToObj##objIndex(point - VEC3(0, 0, NUDGE_EPSILON)), \
			0.0f \
		) \
	)

// Random number and direction generators
// -----------------------
float2 clRand(uint2 *state);
float2 clRand(uint2 *state)
{
    const float2 invMaxInt = (float2) (1.0f/4294967296.0f, 1.0f/4294967296.0f);
    uint x = (*state).x * 17 + (*state).y * 13123;
    (*state).x = (x<<13) ^ x;
    (*state).y ^= (x<<7);

    uint2 tmp = (uint2)
    (
		(x * (x * x * 15731 + 74323) + 871483),
		(x * (x * x * 13734 + 37828) + 234234)
	);

    return convert_float2(tmp) * invMaxInt;
}

// Return random direction on hemiphere around normal.
// This function has: PDF = cos(theta) / pi
float4 rndCosWeightedHemisphereDir(float4 normal, uint2 *seed);
float4 rndCosWeightedHemisphereDir(float4 normal, uint2 *seed) {
	float2 rnd  = clRand(seed);

	// Generate point on disk
	float rd = sqrt(rnd.x);
	float phi = 2.0f*M_PI*rnd.y;

	// Generate tangent, bi-tangent vectors
	float4 u = normalize(cross((fabs(normal.x) > .1f ? (float4)(0.0f, 1.0f, 0.0f, 0.0f) : (float4)(1.0f, 0.0f, 0.0f, 0.0f)), normal));
	float4 v = cross(normal,u);

	// Project disk point to unit hemisphere and rotate so that the normal points up
	return normalize(u * rd * cos(phi) + v * rd * sin(phi) + normal * sqrt(1 - rnd.x));
}

// Tracer implementation
// ---------------------

typedef struct {
	float4 position;
	float4 normal;
	float distFromOrigin;
	int objIndex;
	int matIndex;
} Hit;

typedef struct {
	unsigned int type;
	float4 diffuse;
	float4 emissive;
} Material;

// Material definitions
// The following code will be autogenerated by the scene processor
// ------------------
__constant float4 sceneBgColor = (float4)(0.0f);

__constant Material SceneMaterial[] = {
	{SURFACE_LIGHT, VEC3(0.0f,0.0f,0.0f), VEC3(60.0f,60.0f,60.0f)},
	{SURFACE_DIFFUSE, VEC3(0.75f, 0.25f, 0.25f)},
	{SURFACE_DIFFUSE, VEC3(0.25f, 0.25f, 0.75f)},
	{SURFACE_DIFFUSE, VEC3(0.75f, 0.75f, 0.75f)},
	{SURFACE_SPECULAR, VEC3(0.999f, 0.999f, 0.999f)},
	{SURFACE_REFRACTIVE, VEC3(0.999f, 0.999f, 0.999f)}
};

// Object definitions
// The following code will be autogenerated by the scene processor
// ------------------

// Light
SPHERE_SD_FN(0, VEC3(0.0f, 1.5f, 0.0f), 0.1f)
// Left wall
BOX_SD_FN(1, VEC3(-2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f))
// Right wall
BOX_SD_FN(2, VEC3(2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f))
// Top wall
BOX_SD_FN(3, VEC3(0.0f,2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f))
// Bottom wall
BOX_SD_FN(4, VEC3(0.0f,-2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f))
// Back wall
BOX_SD_FN(5, VEC3(0,0,-2), VEC3(2.0f, 2.0f, 0.1f))
// Mirror ball
SPHERE_SD_FN(6, VEC3(-1.0f, -1.4f, -1.0f), 0.5f)
// Glass ball
SPHERE_SD_FN(7, VEC3(1.4f, -1.4f, -0.5f), 0.5f)

// Estimate normal at intersection point
void getNormal(Hit *hit);
void getNormal(Hit *hit){
	// The following code will be autogenerated by the scene processor
	if( hit->objIndex == 0 ){
		hit->normal = OBJECT_NORMAL_AT(0, hit->position);
	} else if( hit->objIndex == 1 ){
		hit->normal = OBJECT_NORMAL_AT(1, hit->position);
	} else if( hit->objIndex == 2 ){
		hit->normal = OBJECT_NORMAL_AT(2, hit->position);
	} else if( hit->objIndex == 3 ){
		hit->normal = OBJECT_NORMAL_AT(3, hit->position);
	} else if( hit->objIndex == 4 ){
		hit->normal = OBJECT_NORMAL_AT(4, hit->position);
	} else if( hit->objIndex == 5 ){
		hit->normal = OBJECT_NORMAL_AT(5, hit->position);
	} else if( hit->objIndex == 6 ){
		hit->normal = OBJECT_NORMAL_AT(6, hit->position);
	} else if( hit->objIndex == 7 ){
		hit->normal = OBJECT_NORMAL_AT(7, hit->position);
	}
}

// Estimate distance to nearest object
void intersectWorld(const float4 rayOrigin, const float4 rayDir, const float minDist, const float maxDist, Hit *hit);
void intersectWorld(const float4 rayOrigin, const float4 rayDir, const float minDist, const float maxDist, Hit *hit){
	hit->objIndex = NO_SURFACE_HIT;
	hit->matIndex = NO_SURFACE_HIT;

	float4 point;
	float curDist = minDist;
	float objDist, nearestDist;
	int nearestObj = -1;
	int nearestMat = -1;
	for(unsigned int step=0;step<MAX_TRACE_STEPS;step++){
		point = rayOrigin + rayDir * curDist;

		// Find nearest object/dist to point
		// The following code will be autogenerated by the scene processor
		nearestDist = FLT_MAX;
		objDist = distToObj0(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 0;
			nearestMat = 0;
		}
		objDist = distToObj1(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 1;
			nearestMat = 1;
		}
		objDist = distToObj2(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 2;
			nearestMat = 2;
		}
		objDist = distToObj3(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 3;
			nearestMat = 3;
		}
		objDist = distToObj4(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 4;
			nearestMat = 3;
		}
		objDist = distToObj5(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 5;
			nearestMat = 3;
		}
		objDist = distToObj6(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 6;
			nearestMat = 4;
		}
		objDist = distToObj7(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 7;
			nearestMat = 5;
		}

		// If we are inside an object nearestDist will be < 0
		nearestDist = fabs(nearestDist);

		curDist += nearestDist;
		if(curDist > maxDist){
			return;
		}

		// We are close enough to the object to register a hit
		if(nearestDist < DIST_SNAP_EPSILON){
			hit->position = point;
			hit->distFromOrigin = curDist;
			hit->objIndex = nearestObj;
			hit->matIndex = nearestMat;
			return;
		}
	}
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir, uint2 *rndSeed);
float4 traceRay(float4 rayOrigin, float4 rayDir, uint2 *rndSeed){
	Hit hit;
	__constant Material *material;
	float4 rCol = (float4)(0.0f);
	float4 mask = (float4)(1.0f);
	unsigned int bounce = 0;
	for(bounce=0;bounce<MAX_BOUNCES;bounce++){
		intersectWorld(rayOrigin, rayDir, NUDGE_EPSILON, FLT_MAX, &hit);

		// No hit
		if(hit.objIndex == NO_SURFACE_HIT){
			if(bounce == 0){
				rCol = sceneBgColor;
			}
			break;
		}

		// Get material at hit point
		material = &SceneMaterial[hit.matIndex];

		// If we hit a light just add its emissive property and stop
		if( material->type == SURFACE_LIGHT ){
			rCol += mask * material->emissive;
			break;
		}

		// Get normal at hit point
		getNormal(&hit);

		// After some bounces apply a russian roulette to terminate long paths
		if( bounce > MIN_BOUNCES_TO_USE_RR ) {
			float maxComponent = VEC3_MAX_COMPONENT(material->diffuse);
			if( maxComponent == 0 || clRand(rndSeed).x > maxComponent ){
				break;
			}

			// Boost surviving paths by 1/rr_prob
			mask /= maxComponent;
		}

		// Mask outgoing reflectance by material diffuse property
		mask *= material->diffuse;

		// Next bounce starts at hit point
		rayOrigin = hit.position;

		if( material->type == SURFACE_DIFFUSE ) {
			// We do importance sampling for diffuse rays
			rayDir = rndCosWeightedHemisphereDir(hit.normal, rndSeed);
		} else{
			// Reflect ray around normal
			rayDir = normalize(-2.0f * dot(hit.normal, rayDir) * hit.normal + rayDir);
		}

		++bounce;
	}

	return rCol;
}

// Emit the color of a pixel by tracing a ray through the scene.
__kernel void tracePixel(
		__global float4 *frameBuffer,
		__global float4 *frustrumCorners,
		const float4 eyePos,
		const unsigned int blockY,
		const unsigned int samplesPerPixel,
		const float exposure,
		const int seed
		){
	// Get pixel coordinates
	unsigned int x = get_global_id(0);
	unsigned int y = get_global_id(1);
	if ( x > FRAME_W || y > FRAME_H ) {
		return;
	}

	// Setup seed for random numbers
	uint2 rndSeed = (uint2)(x * seed, y * seed);

	// Calculate texel coordinates [0,1] range
	float accumScaler = 1.0f / (float)samplesPerPixel;
	float4 accum = (float4)(0.0f,0.0f,0.0f,0.0f);

	for( uint sample=0;sample<samplesPerPixel;sample++){
		// Apply stratified sampling using a tent filter. This will wrap our
		// random numbers in the [-1, 1] range. X and Y point to the top corner
		// of the current texel so we need to add a bit of offset to get the coords
		// into the [-0.5, 1.5] range.
		float2 random = clRand(&rndSeed);
		float offX = random.x < 0.5f ? sqrt(2.0f * random.x) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.x);
		float offY = random.y < 0.5f ? sqrt(2.0f * random.y) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.y);
		float tx = ((float)x + offX) * TEXEL_STEP_X;
		float ty = ((float)y + offY) * TEXEL_STEP_Y;

		// Get ray direction using trilinear interpolation; trace ray and
		// add result to accumulation buffer
		float4 lVec = frustrumCorners[0] * (1 - ty) + frustrumCorners[2] * ty;
		float4 rVec = frustrumCorners[1] * (1 - ty) + frustrumCorners[3] * ty;
		accum += traceRay(eyePos, normalize(lVec * (1 - tx) + rVec * tx), &rndSeed);
	}

	// Average samples
	accum *= accumScaler;

	// Apply tone-mapping and gamma correction using:
	// 1 - exp(-hdrColor * exposure)) [tone mapping HDR -> LDR]
	// pow(ldr, 0.45)) [gamma correction]
	frameBuffer[y*FRAME_W + x] = pow(-expm1(-accum*exposure), 0.45f);
}


to measure pure memory time

to measure pure memory time


[cl] Cleanup MIS code and fix max_dist rounding bug for occlusion rays

#ifndef PT_INTEGRATOR_KERNEL_CL
#define PT_INTEGRATOR_KERNEL_CL

#define MAX_VEC3_COMPONENT(v) (max(v.x,max(v.y,v.z)))
#define MIN_VEC3_COMPONENT(v) (min(v.x,min(v.y,v.z)))
#define DISPLACE_BY_EPSILON(v,n) (v + n * INTERSECTION_EPSILON)

#define MIN_BOUNCES_FOR_RR 3

#define BALANCE_HEURISTIC(a,b) a/(a+b)
#define POWER_HEURISTIC(a,b) (a*a)/(a*a+b*b)

// For each intersection, calculate an outgoing indirect ray based on the
// surface PDF and also perform direct light sampling emitting occlusion
// rays and light samples.
//
// If a ray hits an emissive surface, we update the accumulator with emissive
// output multiplied by the current throughput and kill the ray.
__kernel void shadeHits(
		__global Ray *rays,
		global const int *numRays,
		__global Path *paths,
		__global uint *hitFlags,
		__global Intersection *intersections,
		// scene data
		__global float4 *vertices,
		__global float4 *normals,
		__global float2 *uv,
		__global uint *materialIndices,
		__global MaterialNode *materialNodes,
		__global Emissive *emissives,
		const uint numEmissives,
		// texture data
		__global TextureMetadata *texMeta,
		__global uchar *texData,
		// state
		const uint bounce,
		const uint randSeed,
		// occlusion rays and samples
		__global Ray *occlusionRays,
		volatile global int *numOcclusionRays,
		__global float3 *emissiveSamples,
		// indirect rays
		__global Ray *indirectRays,
		volatile global int *numIndirectRays,
		// output accumulator
		__global float3 *accumulator
		){

	// Local counters used to perform atomics inside this WG
	volatile __local int wgNumOcclusionRays;
	volatile __local int wgNumIndirectRays;
	int wgOcclusionRayIndex = -1;
	int wgIndirectRayIndex = -1;

	int localId = get_local_id(0);
	int globalId = get_global_id(0);

	// The first thread should initialize the global counters
	if(globalId == 0){
		*numOcclusionRays = 0;
		*numIndirectRays = 0;
	}

	// The first thread in this WG should initialize the local counters
	if(localId == 0){
		wgNumOcclusionRays = 0;
		wgNumIndirectRays = 0;
	}

	barrier(CLK_LOCAL_MEM_FENCE);

	Surface surface;
	int rayPathIndex;
	float3 outRayOrigin;
	float3 curPathThroughput;
	float3 bxdfOutRayDir, bxdfSample, bxdfEmissiveSample, emissiveOutRayDir, emissiveSample;
	float bxdfPdf, bxdfEmissivePdf, emissivePdf, emissiveBxdfPdf, emissiveSelectionPdf;
	float emissiveWeight, bxdfWeight, distToEmissive;

	if(globalId < *numRays){

		if( hitFlags[globalId] ){
			// Init PRNG and generate required samples
			uint2 rndState = (uint2)(randSeed, globalId);
			float2 sample0 = randomGetSample2f(&rndState);
			float2 sample1 = randomGetSample2f(&rndState);
			float2 sample2 = randomGetSample2f(&rndState);

			// Load incoming ray direction and invert it so it points away
			// from the surface. All BxDF formulas use in/out rays that
			// are going outwards from the surface.
			float3 inRayDir = -rayGetDirAndPathIndex(rays + globalId, &rayPathIndex);
			curPathThroughput = paths[rayPathIndex].throughput;

			// Fill surface data and calculate cos(n, inRay)
			surfaceInit(&surface, intersections + globalId, vertices, normals, uv, materialIndices);

			// Select material
			MaterialNode materialNode;
			matSelectNode(&surface, inRayDir, &materialNode, materialNodes, &rndState, texMeta, texData);

			// Apply normal map
			if(materialNode.normalTex != -1){
				surface.normal = matGetNormalSample3f(surface.normal, surface.uv, materialNode.normalTex, texMeta, texData);
			}

			float inRayDotNormal = dot(inRayDir, surface.normal);

			// Check if we hit an emissive node. If so, we need to accumulate implicit
			// light and terminate the path.
			if( BXDF_IS_EMISSIVE(materialNode.bxdfType) ){
				// Make sure that the incoming ray is facing the emissive.
				if( inRayDotNormal > 0.0f ){
					accumulator[rayPathIndex] += curPathThroughput * matGetSample3f(surface.uv, materialNode.kval, materialNode.kvalTex, texMeta, texData);
				}
			} else {
				// Implement RR to terminate paths with no significant contribution
				// killing paths with a probability less than sample2.x while also
				// boosting surving paths by the same probablility.
				bool rejectSample = false;
				if(bounce >= MIN_BOUNCES_FOR_RR) {
					float rrProbability = max(
							// convert throughput to luminance
							min(0.5f, 0.2126f * curPathThroughput.x + 0.7152f * curPathThroughput.y + 0.0722f * curPathThroughput.z),
							0.01f
							);
					if (rrProbability < sample2.x){
						rejectSample = true;
					} else {
						curPathThroughput /= rrProbability;
					}
				}

				if( !rejectSample ){
					// To calculate the origin for occlusion/indirect rays we displace the
					// surface hit point by a small epsilon along the normal to ensure that
					// we don't register an intersection with the same surface.  If this
					// material is refractive and we are hitting it from the outside we
					// need to ensure that the outgoing ray starts inside the surface.
					float displaceDir = BXDF_IS_TRANSMISSION(materialNode.bxdfType) ? -sign(inRayDotNormal) : 1.0f;
					outRayOrigin = DISPLACE_BY_EPSILON(surface.point, surface.normal * displaceDir);

					// Get BXDF sample and generate outgoing ray based on surface BXDF
					bxdfSample = bxdfGetSample(&surface, &materialNode, texMeta, texData, sample0, inRayDir, &bxdfOutRayDir, &bxdfPdf);

					// Select and sample emissive source
					int emissiveIndex = numEmissives > 0 ? emissiveSelect(numEmissives, sample1.x, &emissiveSelectionPdf) : -1;
					if( emissiveIndex > -1 ){
						emissiveSample = emissiveGetSample(&surface, emissives + emissiveIndex, vertices, normals, uv, materialNodes, texMeta, texData, sample1, &emissiveOutRayDir, &emissivePdf, &distToEmissive);

						// MIS: we already have a PDF for generating emissiveOutRayDir.
						// Calculate a PDF for the BXDF sampler generating the same ray
						// and generate sampling weights using the power heuristic.
						bxdfEmissivePdf = bxdfGetPdf(&surface, &materialNode, texMeta, texData, inRayDir, emissiveOutRayDir);
						emissiveWeight = POWER_HEURISTIC(emissivePdf, bxdfEmissivePdf);

						// We use the same approach to calculate a weight for the BXDF sample by
						// calculating the PDF for the emissive sampler generating bxdfOutRayDir
						emissiveBxdfPdf = emissiveGetPdf(&surface, emissives + emissiveIndex, vertices, normals, uv, materialNodes, texMeta, texData, bxdfOutRayDir);
						bxdfWeight = POWER_HEURISTIC(bxdfPdf, emissiveBxdfPdf);
					}

					// If we have a valid emissive sample allocate an occlusion ray.
					float nDotEmissiveOutRay = max(0.0f, dot(surface.normal, emissiveOutRayDir));
					if( MIN_VEC3_COMPONENT(emissiveSample) > 0.0f && emissivePdf > 0.0f && nDotEmissiveOutRay > 0.0f){
						float3 bxdfEmissiveSample = bxdfEval(&surface, &materialNode, texMeta, texData, inRayDir, emissiveOutRayDir);
						emissiveSample *= emissiveWeight * bxdfEmissiveSample / (emissivePdf * emissiveSelectionPdf) * curPathThroughput * nDotEmissiveOutRay;
						wgOcclusionRayIndex = atomic_inc(&wgNumOcclusionRays);
					}

					// If we got a valid bxdf sample update the path throughput
					// Note: we are using the abs value of the dot product as
					// it will be negative for rays entering into refractive surfaces
					float3 throughput = bxdfWeight * bxdfSample * fabs(dot(surface.normal, bxdfOutRayDir));
					if (MIN_VEC3_COMPONENT(throughput) > 0.0f && bxdfPdf > 0.0f){
						pathSetThroughput(paths + rayPathIndex, curPathThroughput * throughput / bxdfPdf);
						wgIndirectRayIndex = atomic_inc(&wgNumIndirectRays);
					}
				} // if(!rejectSample)
			} // if(BXDF_IS_EMISSIVE)
		} // if(hitFlags)
	} // if(globalId < *numRays)

	// When we reach this point, all local threads will have added their occlusion and
	// indirect ray requirements to the local ray counters. The first thread in this
	// WG should perform an atomic_add to the global ray counters to get back an
	// offset which will then be used by the local threads to emit their generated
	// rays.
	barrier(CLK_LOCAL_MEM_FENCE);
	if(localId == 0){
		if( wgNumOcclusionRays > 0 ){
			wgNumOcclusionRays = atomic_add(numOcclusionRays, wgNumOcclusionRays);
		}
		if( wgNumIndirectRays > 0 ){
			wgNumIndirectRays = atomic_add(numIndirectRays, wgNumIndirectRays);
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	// Emit occlusion ray and sample
	if( wgOcclusionRayIndex != -1 && MAX_VEC3_COMPONENT(emissiveSample) > 0.0f ){
		wgOcclusionRayIndex += wgNumOcclusionRays;
		emissiveSamples[wgOcclusionRayIndex] = emissiveSample;
		rayNew(occlusionRays + wgOcclusionRayIndex, outRayOrigin, emissiveOutRayDir, distToEmissive - INTERSECTION_WITH_LIGHT_EPSILON, rayPathIndex);
	}

	// Emit indirect ray
	if( wgIndirectRayIndex != -1 ){
		wgIndirectRayIndex += wgNumIndirectRays;
		rayNew(indirectRays + wgIndirectRayIndex, outRayOrigin, bxdfOutRayDir, FLT_MAX, rayPathIndex);
	}
}

// Shade primary ray misses by sampling the scene background.
__kernel void shadePrimaryRayMisses(
		__global Ray *rays,
		__global const int *numRays,
		__global Path *paths,
		__global uint *hitFlags,
		__global MaterialNode *materialNodes,
		const uint sceneDiffuseMatNodeIndex,
		// Texture data
		__global TextureMetadata *texMeta,
		__global uchar *texData,
		// Output
		__global float3 *accumulator
		){

	int globalId = get_global_id(0);

	// If this thread is inactive or we hit something then ignore
	if( globalId >= *numRays || hitFlags[globalId] ){
		return;
	}

	// Just sample global env map or use scene bg color
	MaterialNode matNode = materialNodes[sceneDiffuseMatNodeIndex];
	int rayPathIndex;
	float2 uv = rayToLatLongUV(rayGetDirAndPathIndex(rays + globalId, &rayPathIndex));

	float3 kd = matGetSample3f(uv, matNode.kval, matNode.kvalTex, texMeta, texData);
	accumulator[rayPathIndex] += kd;
}

// Shade indirect ray misses by sampling the scene background.
__kernel void shadeIndirectRayMisses(
		__global Ray *rays,
		__global const int *numRays,
		__global Path *paths,
		__global uint *hitFlags,
		__global MaterialNode *materialNodes,
		const uint sceneDiffuseMatNodeIndex,
		// Texture data
		__global TextureMetadata *texMeta,
		__global uchar *texData,
		// Output
		__global float3 *accumulator
		){

	int globalId = get_global_id(0);

	// If this thread is inactive or we hit something then ignore
	if( globalId >= *numRays || hitFlags[globalId] ){
		return;
	}

	// Just sample global env map or use scene bg color
	MaterialNode matNode = materialNodes[sceneDiffuseMatNodeIndex];
	int rayPathIndex;
	float2 uv = rayToLatLongUV(rayGetDirAndPathIndex(rays + globalId, &rayPathIndex));

	// As this is an indirect ray we need to multiply the path throughput with the diffuse sample
	// and accumulate that.
	float3 kd = matGetSample3f(uv, matNode.kval, matNode.kvalTex, texMeta, texData);
	accumulator[rayPathIndex] += paths[rayPathIndex].throughput * kd;
}

// Accumulate emissive samples for emissive surfaces that are not occluded.
__kernel void accumulateEmissiveSamples(
		__global Ray *rays,
		__global const int *numRays,
		__global uint *hitFlags,
		__global float3 *emissiveSamples,
		__global float3 *accumulator
		){

	int globalId = get_global_id(0);

	// If this thread is inactive or we hit something then there is no clear line of sight to the emissive
	if( globalId >= *numRays || hitFlags[globalId] ){
		return;
	}

	int pathIndex = rayGetdPathIndex(rays + globalId);
	accumulator[pathIndex] += emissiveSamples[globalId];
}

#endif

#ifndef PT_INTEGRATOR_KERNEL_CL
#define PT_INTEGRATOR_KERNEL_CL

#define MAX_VEC3_COMPONENT(v) (max(v.x,max(v.y,v.z)))
#define MIN_VEC3_COMPONENT(v) (min(v.x,min(v.y,v.z)))
#define DISPLACE_BY_EPSILON(v,n) (v + n * INTERSECTION_EPSILON)

#define MIN_BOUNCES_FOR_RR 3

// For each intersection, calculate an outgoing indirect ray based on the
// surface PDF and also perform direct light sampling emitting occlusion
// rays and light samples.
//
// If a ray hits an emissive surface, we update the accumulator with emissive
// output multiplied by the current throughput and kill the ray.
__kernel void shadeHits(
		__global Ray *rays,
		global const int *numRays,
		__global Path *paths,
		__global uint *hitFlags,
		__global Intersection *intersections,
		// scene data
		__global float4 *vertices,
		__global float4 *normals,
		__global float2 *uv,
		__global uint *materialIndices,
		__global MaterialNode *materialNodes,
		__global Emissive *emissives,
		const uint numEmissives,
		// texture data
		__global TextureMetadata *texMeta,
		__global uchar *texData,
		// state
		const uint bounce,
		const uint randSeed,
		// occlusion rays and samples
		__global Ray *occlusionRays,
		volatile global int *numOcclusionRays,
		__global float3 *emissiveSamples,
		// indirect rays
		__global Ray *indirectRays,
		volatile global int *numIndirectRays,
		// output accumulator
		__global float3 *accumulator
		){

	// Local counters used to perform atomics inside this WG
	volatile __local int wgNumOcclusionRays;
	volatile __local int wgNumIndirectRays;
	int wgOcclusionRayIndex = -1;
	int wgIndirectRayIndex = -1;

	int localId = get_local_id(0);
	int globalId = get_global_id(0);

	// The first thread should initialize the global counters
	if(globalId == 0){
		*numOcclusionRays = 0;
		*numIndirectRays = 0;
	}

	// The first thread in this WG should initialize the local counters
	if(localId == 0){
		wgNumOcclusionRays = 0;
		wgNumIndirectRays = 0;
	}

	barrier(CLK_LOCAL_MEM_FENCE);

	Surface surface;
	int rayPathIndex;
	float3 outRayOrigin;
	float3 curPathThroughput;
	float3 bxdfOutRayDir, bxdfSample;
	float bxdfPdf, bxdfWeight;
	float3 emissiveOutRayDir, emissiveSample;
	float emissiveSelectionPdf, emissivePdf, emissiveBxdfPdf, bxdfEmissivePdf, emissiveWeight, distToEmissive;

	if(globalId < *numRays){

		if( hitFlags[globalId] ){
			// Init PRNG and generate required samples
			uint2 rndState = (uint2)(randSeed, globalId);
			float2 sample0 = randomGetSample2f(&rndState);
			float2 sample1 = randomGetSample2f(&rndState);
			float2 sample2 = randomGetSample2f(&rndState);

			// Load incoming ray direction and invert it so it points away
			// from the surface. All BxDF formulas use in/out rays that
			// are going outwards from the surface.
			float3 inRayDir = -rayGetDirAndPathIndex(rays + globalId, &rayPathIndex);
			curPathThroughput = paths[rayPathIndex].throughput;

			// Fill surface data and calculate cos(n, inRay)
			surfaceInit(&surface, intersections + globalId, vertices, normals, uv, materialIndices);

			// Select material
			MaterialNode materialNode;
			matSelectNode(&surface, inRayDir, &materialNode, materialNodes, &rndState, texMeta, texData);

			// Apply normal map
			if(materialNode.normalTex != -1){
				surface.normal = matGetNormalSample3f(surface.normal, surface.uv, materialNode.normalTex, texMeta, texData);
			}

			float inRayDotNormal = dot(inRayDir, surface.normal);

			// Check if we hit an emissive node. If so, we need to accumulate implicit
			// light and terminate the path.
			if( BXDF_IS_EMISSIVE(materialNode.bxdfType) ){
				// Make sure that the incoming ray is facing the emissive
				if( inRayDotNormal > 0.0f ){
					accumulator[rayPathIndex] += curPathThroughput * matGetSample3f(surface.uv, materialNode.kval, materialNode.kvalTex, texMeta, texData);
				}
			} else {
				// Implement RR to terminate paths with no significant contribution
				// killing paths with a probability less than sample2.x while also
				// boosting surving paths by the same probablility.
				bool rejectSample = false;
				if(bounce >= MIN_BOUNCES_FOR_RR) {
					float rrProbability = max(
							// convert throughput to luminance
							min(0.5f, 0.2126f * curPathThroughput.x + 0.7152f * curPathThroughput.y + 0.0722f * curPathThroughput.z),
							0.01f
							);
					if (rrProbability < sample2.x){
						rejectSample = true;
					} else {
						curPathThroughput /= rrProbability;
					}
				}

				if( !rejectSample ){
					// Get BXDF sample and generate outgoing ray based on surface BXDF
					bxdfSample = bxdfGetSample(
							&surface,
							&materialNode,
							texMeta,
							texData,
							sample0,
							inRayDir,
							&bxdfOutRayDir,
							&bxdfPdf
							);

					// If this material is refractive and we are hitting it from the outside
					// we need to ensure that the outgoing ray starts inside the surface.
					float displaceDir = BXDF_IS_TRANSMISSION(materialNode.bxdfType) ? -sign(inRayDotNormal) : 1.0f;

					// To calculate the origin for occlusion/indirect rays we displace the
					// surface hit point by a small epsilon along the normal to ensure that
					// we don't register an intersection with the same surface
					outRayOrigin = DISPLACE_BY_EPSILON(surface.point, surface.normal * displaceDir);

					// Select and sample emissive source
					int emissiveIndex = numEmissives > 0 ? emissiveSelect(numEmissives, sample1.x, &emissiveSelectionPdf) : -1;
					if( emissiveIndex > -1 ){
						emissiveSample = emissiveGetSample(
								&surface,
								emissives + emissiveIndex,
								vertices,
								normals,
								uv,
								materialNodes,
								texMeta,
								texData,
								sample1,
								&emissiveOutRayDir,
								&emissivePdf,
								&distToEmissive
								);

						// calculate a PDF for the surface BXDF generating the emissiveOutRayDir
						// and a weight for the light sample using Wl = p_a / (p_a + p_b)
						emissiveBxdfPdf = bxdfGetPdf(&surface, &materialNode, texMeta, texData, inRayDir, emissiveOutRayDir);
						emissiveWeight = emissivePdf / (emissivePdf + emissiveBxdfPdf);

						// calculate a PDF for the ray generated with the BXDF sampling strategy
						// intersecting with the selected emissive and a weight for the BXDF sample
						bxdfEmissivePdf = emissiveGetPdf(
								&surface,
								emissives + emissiveIndex,
								vertices,
								normals,
								uv,
								materialNodes,
								texMeta,
								texData,
								bxdfOutRayDir
								);
						bxdfWeight = bxdfPdf / (bxdfPdf + bxdfEmissivePdf);
					}

					// If we have a valid emissive sample allocate an occlusion ray.
					float nDotEmissiveOutRay = max(0.0f, dot(surface.normal, emissiveOutRayDir));
					if( MIN_VEC3_COMPONENT(emissiveSample) > 0.0f && emissivePdf > 0.0f && nDotEmissiveOutRay > 0.0f){
						// Evaluate surface BXDF for the outgoing ray
						// This is light importance sampling
						float3 emissiveBxdf = bxdfEval(
								&surface,
								&materialNode,
								texMeta,
								texData,
								inRayDir,
								emissiveOutRayDir
								);

						emissiveSample = curPathThroughput * emissiveSample * emissiveBxdf * nDotEmissiveOutRay * emissiveWeight / (emissiveSelectionPdf * emissivePdf);
						wgOcclusionRayIndex = atomic_inc(&wgNumOcclusionRays);
					}

					// If we got a valid bxdf sample update the path throughput
					// Note: we are using the abs value of the dot product as
					// it will be negative for rays entering into refractive surfaces
					float3 throughput = bxdfSample * fabs(dot(surface.normal, bxdfOutRayDir)) * bxdfWeight;

					if (MIN_VEC3_COMPONENT(throughput) > 0.0f && bxdfPdf > 0.0f){
						pathSetThroughput(paths + rayPathIndex, curPathThroughput * throughput / bxdfPdf);
						wgIndirectRayIndex = atomic_inc(&wgNumIndirectRays);
					}
				} // if(!rejectSample)
			} // if(BXDF_IS_EMISSIVE)
		} // if(hitFlags)
	} // if(globalId < *numRays)

	// When we reach this point, all local threads will have added their occlusion and
	// indirect ray requirements to the local ray counters. The first thread in this
	// WG should perform an atomic_add to the global ray counters to get back an
	// offset which will then be used by the local threads to emit their generated
	// rays.
	barrier(CLK_LOCAL_MEM_FENCE);
	if(localId == 0){
		if( wgNumOcclusionRays > 0 ){
			wgNumOcclusionRays = atomic_add(numOcclusionRays, wgNumOcclusionRays);
		}
		if( wgNumIndirectRays > 0 ){
			wgNumIndirectRays = atomic_add(numIndirectRays, wgNumIndirectRays);
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	// Emit occlusion ray and sample
	if( wgOcclusionRayIndex != -1 && MAX_VEC3_COMPONENT(emissiveSample) > 0.0f ){
		wgOcclusionRayIndex += wgNumOcclusionRays;
		emissiveSamples[wgOcclusionRayIndex] = emissiveSample;
		rayNew(occlusionRays + wgOcclusionRayIndex, outRayOrigin, emissiveOutRayDir, distToEmissive - INTERSECTION_EPSILON_X2, rayPathIndex);
	}

	// Emit indirect ray
	if( wgIndirectRayIndex != -1 ){
		wgIndirectRayIndex += wgNumIndirectRays;
		rayNew(indirectRays + wgIndirectRayIndex, outRayOrigin, bxdfOutRayDir, FLT_MAX, rayPathIndex);
	}
}

// Shade primary ray misses by sampling the scene background.
__kernel void shadePrimaryRayMisses(
		__global Ray *rays,
		__global const int *numRays,
		__global Path *paths,
		__global uint *hitFlags,
		__global MaterialNode *materialNodes,
		const uint sceneDiffuseMatNodeIndex,
		// Texture data
		__global TextureMetadata *texMeta,
		__global uchar *texData,
		// Output
		__global float3 *accumulator
		){

	int globalId = get_global_id(0);

	// If this thread is inactive or we hit something then ignore
	if( globalId >= *numRays || hitFlags[globalId] ){
		return;
	}

	// Just sample global env map or use scene bg color
	MaterialNode matNode = materialNodes[sceneDiffuseMatNodeIndex];
	int rayPathIndex;
	float2 uv = rayToLatLongUV(rayGetDirAndPathIndex(rays + globalId, &rayPathIndex));

	float3 kd = matGetSample3f(uv, matNode.kval, matNode.kvalTex, texMeta, texData);
	accumulator[rayPathIndex] += kd;
}

// Shade indirect ray misses by sampling the scene background.
__kernel void shadeIndirectRayMisses(
		__global Ray *rays,
		__global const int *numRays,
		__global Path *paths,
		__global uint *hitFlags,
		__global MaterialNode *materialNodes,
		const uint sceneDiffuseMatNodeIndex,
		// Texture data
		__global TextureMetadata *texMeta,
		__global uchar *texData,
		// Output
		__global float3 *accumulator
		){

	int globalId = get_global_id(0);

	// If this thread is inactive or we hit something then ignore
	if( globalId >= *numRays || hitFlags[globalId] ){
		return;
	}

	// Just sample global env map or use scene bg color
	MaterialNode matNode = materialNodes[sceneDiffuseMatNodeIndex];
	int rayPathIndex;
	float2 uv = rayToLatLongUV(rayGetDirAndPathIndex(rays + globalId, &rayPathIndex));

	// As this is an indirect ray we need to multiply the path throughput with the diffuse sample
	// and accumulate that.
	float3 kd = matGetSample3f(uv, matNode.kval, matNode.kvalTex, texMeta, texData);
	accumulator[rayPathIndex] += paths[rayPathIndex].throughput * kd;
}

// Accumulate emissive samples for emissive surfaces that are not occluded.
__kernel void accumulateEmissiveSamples(
		__global Ray *rays,
		__global const int *numRays,
		__global uint *hitFlags,
		__global float3 *emissiveSamples,
		__global float3 *accumulator
		){

	int globalId = get_global_id(0);

	// If this thread is inactive or we hit something then there is no clear line of sight to the emissive
	if( globalId >= *numRays || hitFlags[globalId] ){
		return;
	}

	int pathIndex = rayGetdPathIndex(rays + globalId);
	accumulator[pathIndex] += emissiveSamples[globalId];
}

#endif