Update SpGEMM_EM_kernels.cl

Update SpGEMM_EM_kernels.cl

/* ************************************************************************
* The MIT License (MIT)
* Copyright 2014-2015 weifengliu

*  Permission is hereby granted, free of charge, to any person obtaining a copy
*  of this software and associated documentation files (the "Software"), to deal
*  in the Software without restriction, including without limitation the rights
*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the Software is
*  furnished to do so, subject to the following conditions:

*  The above copyright notice and this permission notice shall be included in
*  all copies or substantial portions of the Software.

*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*  THE SOFTWARE.
* ************************************************************************ */
//////////////////////////////////////////////////////////////////////////
// < A CUDA/OpenCL General Sparse Matrix-Matrix Multiplication Program >
//
// < See paper:
// Weifeng Liu and Brian Vinter, "An Efficient GPU General Sparse
// Matrix-Matrix Multiplication for Irregular Data," Parallel and
// Distributed Processing Symposium, 2014 IEEE 28th International
// (IPDPS '14), pp.370-381, 19-23 May 2014
// for details. >
//////////////////////////////////////////////////////////////////////////

#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable
#pragma OPENCL EXTENSION cl_khr_fp64 : enable

#define TUPLE_QUEUE 6
typedef double   vT;

inline
void binarysearch(__local int   *s_key,
                  __local vT *s_val,
                  int            key_input,
                  vT          val_input,
                  int            merged_size,
                  bool          *is_new_col)
{
    int start = 0;
    int stop  = merged_size - 1;
    int median;
    int key_median;

    while (stop >= start)
    {
        median = (stop + start) / 2;
        key_median = s_key[median];

        if (key_input > key_median)
            start = median + 1;
        else if (key_input < key_median)
            stop = median - 1;
        else
        {
            // atomicAdd is not needed since duplicate is not existed in each input row
            s_val[median] += val_input;
            *is_new_col = 0;
            break;
        }
    }
    //return start;
}

inline
void binarysearch_sub(__local int   *s_key,
                  __local vT *s_val,
                  int            key_input,
                  vT          val_input,
                  int            merged_size)
{
    int start = 0;
    int stop  = merged_size - 1;
    int median;
    int key_median;

    while (stop >= start)
    {
        median = (stop + start) / 2;
        key_median = s_key[median];

        if (key_input > key_median)
            start = median + 1;
        else if (key_input < key_median)
            stop = median - 1;
        else
        {
            // atomicAdd is not needed since duplicate is not existed in each input row
            s_val[median] -= val_input;
            break;
        }
    }
    //return start;
}

inline
void binarysearch_global(__global int   *d_key,
                  __global vT *d_val,
                  int            key_input,
                  vT          val_input,
                  int            merged_size,
                  bool          *is_new_col)
{
    int start = 0;
    int stop  = merged_size - 1;
    int median;
    int key_median;

    while (stop >= start)
    {
        median = (stop + start) / 2;
        key_median = d_key[median];

        if (key_input > key_median)
            start = median + 1;
        else if (key_input < key_median)
            stop = median - 1;
        else
        {
            // atomicAdd is not needed since duplicate is not existed in each input row
            d_val[median] += val_input;
            *is_new_col = 0;
            break;
        }
    }
    //return start;
}

inline
void binarysearch_global_sub(__global int   *d_key,
                  __global vT *d_val,
                  int            key_input,
                  vT          val_input,
                  int            merged_size)
{
    int start = 0;
    int stop  = merged_size - 1;
    int median;
    int key_median;

    while (stop >= start)
    {
        median = (stop + start) / 2;
        key_median = d_key[median];

        if (key_input > key_median)
            start = median + 1;
        else if (key_input < key_median)
            stop = median - 1;
        else
        {
            // atomicAdd is not needed since duplicate is not existed in each input row
            d_val[median] -= val_input;
            break;
        }
    }
    //return start;
}

inline
void scan_32(__local volatile short *s_scan,
              const int      local_id)
{
    int ai, bi;
    int baseai = 1 + 2 * local_id;
    int basebi = baseai + 1;
    short temp;

    if (local_id < 16)  { ai = baseai - 1;     bi = basebi - 1;     s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 8)  { ai = 2 * baseai - 1;  bi = 2 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 4)  { ai = 4 * baseai - 1;  bi = 4 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    if (local_id < 2)  { ai = 8 * baseai - 1;  bi = 8 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    if (local_id == 0) { s_scan[31] += s_scan[15]; s_scan[32] = s_scan[31]; s_scan[31] = 0; temp = s_scan[15]; s_scan[15] = 0; s_scan[31] += temp; }
    if (local_id < 2)  { ai = 8 * baseai - 1;  bi = 8 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    if (local_id < 4)  { ai = 4 * baseai - 1;  bi = 4 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    if (local_id < 8)  { ai = 2 * baseai - 1;  bi = 2 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 16)  { ai = baseai - 1;   bi = basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp; }
}

inline
void scan_64(__local volatile short *s_scan,
              const int      local_id)
{
    int ai, bi;
    int baseai = 1 + 2 * local_id;
    int basebi = baseai + 1;
    short temp;

    if (local_id < 32) { ai = baseai - 1;     bi = basebi - 1;     s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 16) { ai =  2 * baseai - 1;  bi =  2 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 8)  { ai = 4 * baseai - 1;  bi = 4 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 4)  { ai = 8 * baseai - 1;  bi = 8 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    if (local_id < 2)  { ai = 16 * baseai - 1;  bi = 16 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    if (local_id == 0) { s_scan[63] += s_scan[31]; s_scan[64] = s_scan[63]; s_scan[63] = 0; temp = s_scan[31]; s_scan[31] = 0; s_scan[63] += temp; }
    if (local_id < 2)  { ai = 16 * baseai - 1;  bi = 16 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    if (local_id < 4)  { ai = 8 * baseai - 1;  bi = 8 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    if (local_id < 8)  { ai = 4 * baseai - 1;  bi = 4 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 16) { ai =  2 * baseai - 1;  bi =  2 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 32) { ai = baseai - 1;   bi = basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp; }
}

inline
void scan_128(__local volatile short *s_scan,
              const int      local_id)
{
    int ai, bi;
    int baseai = 1 + 2 * local_id;
    int basebi = baseai + 1;
    short temp;

    if (local_id < 64) { ai = baseai - 1;     bi = basebi - 1;     s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 32) { ai =  2 * baseai - 1;  bi =  2 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 16) { ai =  4 * baseai - 1;  bi =  4 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 8)  { ai = 8 * baseai - 1;  bi = 8 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 4)  { ai = 16 * baseai - 1;  bi = 16 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    if (local_id < 2)  { ai = 32 * baseai - 1;  bi = 32 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    if (local_id == 0) { s_scan[127] += s_scan[63]; s_scan[128] = s_scan[127]; s_scan[127] = 0; temp = s_scan[63]; s_scan[63] = 0; s_scan[127] += temp; }
    if (local_id < 2)  { ai = 32 * baseai - 1;  bi = 32 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    if (local_id < 4)  { ai = 16 * baseai - 1;  bi = 16 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    if (local_id < 8)  { ai = 8 * baseai - 1;  bi = 8 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 16) { ai =  4 * baseai - 1;  bi =  4 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 32) { ai =  2 * baseai - 1;  bi =  2 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 64) { ai = baseai - 1;   bi = basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp; }
}

inline
void scan_256(__local volatile short *s_scan,
              const int      local_id)
{
    int ai, bi;
    int baseai = 1 + 2 * local_id;
    int basebi = baseai + 1;
    short temp;

    if (local_id < 128) { ai = baseai - 1;     bi = basebi - 1;     s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 64) { ai =  2 * baseai - 1;  bi =  2 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 32) { ai =  4 * baseai - 1;  bi =  4 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 16) { ai =  8 * baseai - 1;  bi =  8 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 8)  { ai = 16 * baseai - 1;  bi = 16 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 4)  { ai = 32 * baseai - 1;  bi = 32 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    if (local_id < 2)  { ai = 64 * baseai - 1;  bi = 64 * basebi - 1;   s_scan[bi] += s_scan[ai]; }
    if (local_id == 0) { s_scan[255] += s_scan[127]; s_scan[256] = s_scan[255]; s_scan[255] = 0; temp = s_scan[127]; s_scan[127] = 0; s_scan[255] += temp; }
    if (local_id < 2)  { ai = 64 * baseai - 1;  bi = 64 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    if (local_id < 4)  { ai = 32 * baseai - 1;  bi = 32 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    if (local_id < 8)  { ai = 16 * baseai - 1;  bi = 16 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 16) { ai =  8 * baseai - 1;  bi =  8 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 32) { ai =  4 * baseai - 1;  bi =  4 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 64) { ai =  2 * baseai - 1;  bi =  2 * basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp;}
    barrier(CLK_LOCAL_MEM_FENCE);
    if (local_id < 128) { ai = baseai - 1;   bi = basebi - 1;   temp = s_scan[ai]; s_scan[ai] = s_scan[bi]; s_scan[bi] += temp; }
}

inline
void scan(__local volatile short *s_scan,
          const int               local_id,
          const int               local_size)
{
    switch (local_size)
    {
    case 32:
        scan_32(s_scan, local_id);
        break;
    case 64:
        scan_64(s_scan, local_id);
        break;
    case 128:
        scan_128(s_scan, local_id);
        break;
    case 256:
        scan_256(s_scan, local_id);
        break;
    }
}

inline
bool comp(int a, int b)
{
    return a < b ? true : false;
}

inline
int mergepath_partition(__local int  *a,
                        const int      aCount,
                        __local int  *b,
                        const int      bCount,
                        const int      diag)
{
    int begin = max(0, diag - bCount);
    int end   = min(diag, aCount);
    int mid;
    int key_a, key_b;
    bool pred;

    while(begin < end)
    {
        mid = (begin + end) >> 1;

        key_a = a[mid];
        key_b = b[diag - 1 - mid];

        pred = comp(key_a, key_b);

        if(pred)
            begin = mid + 1;
        else
            end = mid;
    }
    return begin;
}

inline
void mergepath_serialmerge(__local int   *s_key,
                           __local vT *s_val,
                           int            aBegin,
                           const int      aEnd,
                           int            bBegin,
                           const int      bEnd,
                           int           *reg_key,
                           vT         *reg_val,
                           const int      VT)
{
    int  key_a = s_key[aBegin];
    int  key_b = s_key[bBegin];
    bool p;

    for(int i = 0; i < VT; ++i)
    {
        p = (bBegin >= bEnd) || ((aBegin < aEnd) && !comp(key_b, key_a));

        reg_key[i] = p ? key_a : key_b;
        reg_val[i] = p ? s_val[aBegin] : s_val[bBegin];

        if(p)
            key_a = s_key[++aBegin];
        else
            key_b = s_key[++bBegin];
    }
}

inline
void mergepath(__local int   *s_key,
               __local vT *s_val,
               int           *reg_key,
               vT         *reg_val,
               const int      size_a,
               const int      size_b)
{
    int VT = ceil((size_a + size_b) / (float)get_local_size(0));
    int diag = VT * get_local_id(0);
    int mp = mergepath_partition(s_key, size_a, &s_key[size_a], size_b, diag);

    mergepath_serialmerge(s_key, s_val,
                          mp, size_a, size_a + diag - mp, size_a + size_b,
                          reg_key, reg_val, VT);
    barrier(CLK_LOCAL_MEM_FENCE);

    for (int is = 0; is < VT; is++)
    {
        s_key[diag + is] = reg_key[is];
        s_val[diag + is] = reg_val[is];
    }
}

inline
int mergepath_partition_global(__global int  *a,
                        const int      aCount,
                        __global int  *b,
                        const int      bCount,
                        const int      diag)
{
    int begin = max(0, diag - bCount);
    int end   = min(diag, aCount);
    int mid;
    int key_a, key_b;
    bool pred;

    while(begin < end)
    {
        mid = (begin + end) >> 1;

        key_a = a[mid];
        key_b = b[diag - 1 - mid];

        pred = comp(key_a, key_b);

        if(pred)
            begin = mid + 1;
        else
            end = mid;
    }
    return begin;
}

inline
void mergepath_serialmerge_global(__global int   *s_key,
                           __global vT *s_val,
                           int            aBegin,
                           const int      aEnd,
                           int            bBegin,
                           const int      bEnd,
                           int           *reg_key,
                           vT         *reg_val,
                           const int      VT)
{
    int  key_a = s_key[aBegin];
    int  key_b = s_key[bBegin];
    bool p;

    for(int i = 0; i < VT; ++i)
    {
        p = (bBegin >= bEnd) || ((aBegin < aEnd) && !comp(key_b, key_a));

        reg_key[i] = p ? key_a : key_b;
        reg_val[i] = p ? s_val[aBegin] : s_val[bBegin];

        if(p)
            key_a = s_key[++aBegin];
        else
            key_b = s_key[++bBegin];
    }
}

inline
void readwrite_mergedlist(__global int   *d_csrColIndCt,
                          __global vT *d_csrValCt,
                          __local  int   *s_key_merged,
                          __local  vT *s_val_merged,
                          const int       merged_size,
                          const int       row_offset,
                          const bool      is_write)
{
    int local_id = get_local_id(0);
    int local_size = get_local_size(0);

    int stride, offset_local_id, global_offset;
    int loop = ceil((float)merged_size / (float)local_size);

    for (int i = 0; i < loop; i++)
    {
        stride = i != loop - 1 ? local_size : merged_size - i * local_size;
        offset_local_id = i * local_size + local_id;
        global_offset = row_offset + offset_local_id;

        if (local_id < stride)
        {
            if (is_write)
            {
                d_csrColIndCt[global_offset] = s_key_merged[offset_local_id];
                d_csrValCt[global_offset]    = s_val_merged[offset_local_id];
            }
            else
            {
                s_key_merged[offset_local_id] = d_csrColIndCt[global_offset];
                s_val_merged[offset_local_id] = d_csrValCt[global_offset];
            }
        }
    }
}

inline
void readwrite_mergedlist_global(__global int   *d_csrColIndCt,
                          __global vT *d_csrValCt,
                          __global  int   *d_key_merged,
                          __global  vT *d_val_merged,
                          const int       merged_size,
                          const int       row_offset,
                          const bool      is_write)
{
    int local_id = get_local_id(0);
    int local_size = get_local_size(0);

    int stride, offset_local_id, global_offset;
    int loop = ceil((float)merged_size / (float)local_size);

    for (int i = 0; i < loop; i++)
    {
        stride = i != loop - 1 ? local_size : merged_size - i * local_size;
        offset_local_id = i * local_size + local_id;
        global_offset = row_offset + offset_local_id;

        if (local_id < stride)
        {
            if (is_write)
            {
                d_csrColIndCt[global_offset] = d_key_merged[offset_local_id];
                d_csrValCt[global_offset]    = d_val_merged[offset_local_id];
            }
            else
            {
                d_key_merged[offset_local_id] = d_csrColIndCt[global_offset];
                d_val_merged[offset_local_id] = d_csrValCt[global_offset];
            }
        }
    }
}

__kernel
void EM_mergepath(__global int            *d_queue,
                  __global const int      *d_csrRowPtrA,
                  __global const int      *d_csrColIndA,
                  __global const vT    *d_csrValA,
                  __global const int      *d_csrRowPtrB,
                  __global const int      *d_csrColIndB,
                  __global const vT    *d_csrValB,
                  __global int            *d_csrRowPtrC,
                  __global const int      *d_csrRowPtrCt,
                  __global int            *d_csrColIndCt,
                  __global vT          *d_csrValCt,
                  __local  int            *s_key_merged,        // BUFFSIZE_MERGED + 1
                  __local  vT          *s_val_merged,        // BUFFSIZE_MERGED + 1
                  __local  volatile short *s_scan,              // LOCAL_SIZE + 1
                  const int                queue_offset,
                  const int                mergebuffer_size)
{
    int queue_id = TUPLE_QUEUE * (queue_offset + get_group_id(0));

    // if merged size equals -1, kernel return since this row is done
    int merged_size = d_queue[queue_id + 2];

    int local_id = get_local_id(0); //threadIdx.x;
    int row_id = d_queue[queue_id];

    int   local_size = get_local_size(0);
    float local_size_float = local_size;

    int stride, loop;
    int reg_reuse1;

    int   col_Ct;      // index_type
    vT val_Ct;      // value_type
    vT val_A;       // value_type

    int start_col_index_A, stop_col_index_A;  // index_type
    int start_col_index_B, stop_col_index_B;  // index_type

    int k, is;

    bool  is_new_col;
    bool  is_last;
    int   VT, diag, mp;
    int   reg_key[16];
    vT reg_val[16];

    start_col_index_A = d_csrRowPtrA[row_id];
    stop_col_index_A  = d_csrRowPtrA[row_id + 1];

    if (merged_size == 0)
    {
        is_last = false;

        // read the first set of current nnzCt row to merged list
        reg_reuse1 = d_csrColIndA[start_col_index_A];      // reg_reuse1 = row_id_B
        val_A    = d_csrValA[start_col_index_A];

        start_col_index_B = d_csrRowPtrB[reg_reuse1];      // reg_reuse1 = row_id_B
        stop_col_index_B  = d_csrRowPtrB[reg_reuse1 + 1];  // reg_reuse1 = row_id_B

        stride = stop_col_index_B - start_col_index_B;
        loop   = ceil(stride / local_size_float); //ceil((float)stride / (float)local_size);

        start_col_index_B += local_id;

        for (k = 0; k < loop; k++)
        {
            reg_reuse1 = k != loop - 1 ? local_size : stride - k * local_size; // reg_reuse1 = input_size

            // if merged_size + reg_reuse1 > mergebuffer_size, write it to global mem and return
            if (merged_size + reg_reuse1 > mergebuffer_size)
            {
                // write a signal to some place, not equals -1 means next round is needed
                if (local_id == 0)
                {
                    d_queue[queue_id + 2] = merged_size;
                    d_queue[queue_id + 3] = start_col_index_A;
                    d_queue[queue_id + 4] = start_col_index_B;
                }

                // dump current data to global mem
                reg_reuse1 = d_queue[queue_id + 1];
                readwrite_mergedlist(d_csrColIndCt, d_csrValCt, s_key_merged, s_val_merged, merged_size, reg_reuse1, 1);

                return;
            }

            if (start_col_index_B < stop_col_index_B)
            {
                col_Ct = d_csrColIndB[start_col_index_B];
                val_Ct = d_csrValB[start_col_index_B] * val_A;

                s_key_merged[merged_size + local_id] = col_Ct;
                s_val_merged[merged_size + local_id] = val_Ct;
            }
            barrier(CLK_LOCAL_MEM_FENCE);

            merged_size += reg_reuse1;   // reg_reuse1 = input_size
            start_col_index_B += local_size;
        }

        start_col_index_A++;
    }
    else
    {
        is_last = true;
        start_col_index_A = d_queue[queue_id + 3];

        // load existing merged list
        reg_reuse1 = d_queue[queue_id + 5];
        readwrite_mergedlist(d_csrColIndCt, d_csrValCt, s_key_merged, s_val_merged, merged_size, reg_reuse1, 0);
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // merge the rest of sets of current nnzCt row to the merged list
    while (start_col_index_A < stop_col_index_A)
    {
        reg_reuse1 = d_csrColIndA[start_col_index_A];                      // reg_reuse1 = row_id_B
        val_A    = d_csrValA[start_col_index_A];

        start_col_index_B = is_last ? d_queue[queue_id + 4] : d_csrRowPtrB[reg_reuse1];      // reg_reuse1 = row_id_B
        is_last = false;
        stop_col_index_B  = d_csrRowPtrB[reg_reuse1 + 1];  // reg_reuse1 = row_id_B

        stride = stop_col_index_B - start_col_index_B;
        loop  = ceil(stride / local_size_float); //ceil((float)stride / (float)local_size);

        start_col_index_B += local_id;

        for (k = 0; k < loop; k++)
        {
            barrier(CLK_LOCAL_MEM_FENCE);
            is_new_col = 0;

            if (start_col_index_B < stop_col_index_B)
            {
                col_Ct = d_csrColIndB[start_col_index_B];
                val_Ct = d_csrValB[start_col_index_B] * val_A;

                // binary search on existing sorted list
                // if the column is existed, add the value to the position
                // else, set scan value to 1, and wait for scan
                is_new_col = 1;
                binarysearch(s_key_merged, s_val_merged, col_Ct, val_Ct, merged_size, &is_new_col);
            }

            s_scan[local_id] = is_new_col;
            barrier(CLK_LOCAL_MEM_FENCE);

            // scan with half-local_size work-items
            // s_scan[local_size] is the size of input non-duplicate array
            scan(s_scan, local_id, local_size);
            barrier(CLK_LOCAL_MEM_FENCE);

            // if all elements are absorbed into merged list,
            // the following work in this inner-loop is not needed any more
            if (s_scan[local_size] == 0)
            {
                start_col_index_B += local_size;
                continue;
            }

            // check if the total size is larger than the capicity of merged list
            if (merged_size + s_scan[local_size] > mergebuffer_size)
            {
                // roll back 'binary serach plus' in this round
                if (start_col_index_B < stop_col_index_B)
                {
                    binarysearch_sub(s_key_merged, s_val_merged, col_Ct, val_Ct, merged_size);
                }
                barrier(CLK_LOCAL_MEM_FENCE);

                // write a signal to some place, not equals -1 means next round is needed
                if (local_id == 0)
                {
                    d_queue[queue_id + 2] = merged_size;
                    d_queue[queue_id + 3] = start_col_index_A;
                    d_queue[queue_id + 4] = start_col_index_B;
                }

                // dump current data to global mem
                reg_reuse1 = d_queue[queue_id + 1]; //d_csrRowPtrCt[row_id];
                readwrite_mergedlist(d_csrColIndCt, d_csrValCt, s_key_merged, s_val_merged, merged_size, reg_reuse1, 1);

                return;
            }

            // write compact input to free place in merged list
            if(is_new_col)
            {
                reg_reuse1 = merged_size + s_scan[local_id];
                s_key_merged[reg_reuse1] = col_Ct;
                s_val_merged[reg_reuse1] = val_Ct;
            }
            barrier(CLK_LOCAL_MEM_FENCE);

            // merge path partition
            reg_reuse1 = s_scan[local_size]; // reg_reuse1 = size_b;

            VT = ceil((merged_size + reg_reuse1) / local_size_float);

            diag = VT * local_id;
            mp = mergepath_partition(s_key_merged, merged_size, &s_key_merged[merged_size], reg_reuse1, diag);

            mergepath_serialmerge(s_key_merged, s_val_merged,
                                  mp, merged_size, merged_size + diag - mp, merged_size + reg_reuse1,
                                  reg_key, reg_val, VT);
            barrier(CLK_LOCAL_MEM_FENCE);

            for (is = 0; is < VT; is++)
            {
                s_key_merged[diag + is] = reg_key[is];
                s_val_merged[diag + is] = reg_val[is];
            }
            barrier(CLK_LOCAL_MEM_FENCE);

            merged_size += reg_reuse1; // reg_reuse1 = size_b = s_scan[local_size];
            start_col_index_B += local_size;
        }

        start_col_index_A++;
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    if (local_id == 0)
    {
        d_csrRowPtrC[row_id] = merged_size;
        d_queue[queue_id + 2] = -1;
    }

    // write merged list to global mem
    reg_reuse1 = d_queue[queue_id + 1]; //d_csrRowPtrCt[row_id];
    readwrite_mergedlist(d_csrColIndCt, d_csrValCt, s_key_merged, s_val_merged, merged_size, reg_reuse1, 1);
}

__kernel
void EM_mergepath_global(__global int            *d_queue,
                  __global const int      *d_csrRowPtrA,
                  __global const int      *d_csrColIndA,
                  __global const vT    *d_csrValA,
                  __global const int      *d_csrRowPtrB,
                  __global const int      *d_csrColIndB,
                  __global const vT    *d_csrValB,
                  __global int            *d_csrRowPtrC,
                  __global const int      *d_csrRowPtrCt,
                  __global int            *d_csrColIndCt,
                  __global vT          *d_csrValCt,
                  __local  int            *s_key_merged_l1,        // mergebuffer_size + 1
                  __local  vT          *s_val_merged_l1,        // mergebuffer_size + 1
                  __local  volatile short *s_scan,              // LOCAL_SIZE + 1
                  const int                queue_offset,
                  const int                mergebuffer_size)
{
    int queue_id = TUPLE_QUEUE * (queue_offset + get_group_id(0));

    // if merged size equals -1, kernel return since this row is done
    int merged_size_l2 = d_queue[queue_id + 2];
    int merged_size_l1 = 0;

    int local_id = get_local_id(0); //threadIdx.x;
    int row_id = d_queue[queue_id];

    int   local_size = get_local_size(0);
    float local_size_float = local_size;

    int stride, loop;
    int reg_reuse1;

    int   col_Ct;      // index_type
    vT val_Ct;      // value_type
    vT val_A;       // value_type

    int start_col_index_A, stop_col_index_A;  // index_type
    int start_col_index_B, stop_col_index_B;  // index_type

    int k, is;

    bool  is_new_col;
    bool  is_last;
    int   VT, diag, mp;
    int   reg_key[80];
    vT reg_val[80];

    start_col_index_A = d_csrRowPtrA[row_id];
    stop_col_index_A  = d_csrRowPtrA[row_id + 1];

    is_last = true;
    start_col_index_A = d_queue[queue_id + 3];

    // load existing merged list
    reg_reuse1 = d_queue[queue_id + 1];
    __global int   *d_key_merged = &d_csrColIndCt[reg_reuse1];
    __global vT *d_val_merged = &d_csrValCt[reg_reuse1];

    reg_reuse1 = d_queue[queue_id + 5];
    readwrite_mergedlist_global(d_csrColIndCt, d_csrValCt, d_key_merged, d_val_merged, merged_size_l2, reg_reuse1, 0);
    barrier(CLK_GLOBAL_MEM_FENCE);

    // merge the rest of sets of current nnzCt row to the merged list
    while (start_col_index_A < stop_col_index_A)
    {
        reg_reuse1 = d_csrColIndA[start_col_index_A];                      // reg_reuse1 = row_id_B
        val_A    = d_csrValA[start_col_index_A];

        start_col_index_B = is_last ? d_queue[queue_id + 4] : d_csrRowPtrB[reg_reuse1];      // reg_reuse1 = row_id_B
        is_last = false;
        stop_col_index_B  = d_csrRowPtrB[reg_reuse1 + 1];  // reg_reuse1 = row_id_B

        stride = stop_col_index_B - start_col_index_B;
        loop  = ceil(stride / local_size_float); //ceil((float)stride / (float)local_size);

        start_col_index_B += local_id;

        for (k = 0; k < loop; k++)
        {
            barrier(CLK_LOCAL_MEM_FENCE);
            is_new_col = 0;

            if (start_col_index_B < stop_col_index_B)
            {
                col_Ct = d_csrColIndB[start_col_index_B];
                val_Ct = d_csrValB[start_col_index_B] * val_A;

                // binary search on existing sorted list
                // if the column is existed, add the value to the position
                // else, set scan value to 1, and wait for scan
                is_new_col = 1;

                // search on l2
                binarysearch_global(d_key_merged, d_val_merged, col_Ct, val_Ct, merged_size_l2, &is_new_col);

                // search on l1
                if (is_new_col == 1)
                    binarysearch(s_key_merged_l1, s_val_merged_l1, col_Ct, val_Ct, merged_size_l1, &is_new_col);
            }

            s_scan[local_id] = is_new_col;
            barrier(CLK_LOCAL_MEM_FENCE);

            // scan with half-local_size work-items
            // s_scan[local_size] is the size of input non-duplicate array
            scan(s_scan, local_id, local_size);
            barrier(CLK_LOCAL_MEM_FENCE);

            // if all elements are absorbed into merged list,
            // the following work in this inner-loop is not needed any more
            if (s_scan[local_size] == 0)
            {
                start_col_index_B += local_size;
                continue;
            }

            // check if the total size is larger than the capicity of merged list
            if (merged_size_l1 + s_scan[local_size] > mergebuffer_size)
            {
                if (start_col_index_B < stop_col_index_B)
                {
                    // rollback on l2
                    binarysearch_global_sub(d_key_merged, d_val_merged, col_Ct, val_Ct, merged_size_l2);

                    // rollback on l1
                    binarysearch_sub(s_key_merged_l1, s_val_merged_l1, col_Ct, val_Ct, merged_size_l1);
                }
                barrier(CLK_LOCAL_MEM_FENCE);

                // write a signal to some place, not equals -1 means next round is needed
                if (local_id == 0)
                {
                    d_queue[queue_id + 2] = merged_size_l2 + merged_size_l1;
                    d_queue[queue_id + 3] = start_col_index_A;
                    d_queue[queue_id + 4] = start_col_index_B;
                }

                // dump l1 to global
                readwrite_mergedlist(d_key_merged, d_val_merged, s_key_merged_l1, s_val_merged_l1,
                                     merged_size_l1, merged_size_l2, 1);
                barrier(CLK_GLOBAL_MEM_FENCE);

                // merge l2 + l1 on global
                VT = ceil((merged_size_l2 + merged_size_l1) / local_size_float);
                diag = VT * local_id;
                mp = mergepath_partition_global(d_key_merged, merged_size_l2,
                                                &d_key_merged[merged_size_l2], merged_size_l1, diag);

                mergepath_serialmerge_global(d_key_merged, d_val_merged,
                                      mp, merged_size_l2, merged_size_l2 + diag - mp, merged_size_l2 + merged_size_l1,
                                      reg_key, reg_val, VT);
                barrier(CLK_GLOBAL_MEM_FENCE);

                for (is = 0; is < VT; is++)
                {
                    d_key_merged[diag + is] = reg_key[is];
                    d_val_merged[diag + is] = reg_val[is];
                }

                return;
            }

            // write compact input to free place in merged list
            if(is_new_col)
            {
                reg_reuse1 = merged_size_l1 + s_scan[local_id];
                s_key_merged_l1[reg_reuse1] = col_Ct;
                s_val_merged_l1[reg_reuse1] = val_Ct;
            }
            barrier(CLK_LOCAL_MEM_FENCE);

            // merge path partition on l1
            reg_reuse1 = s_scan[local_size]; // reg_reuse1 = size_b;

            VT = ceil((merged_size_l1 + reg_reuse1) / local_size_float);
            diag = VT * local_id;
            mp = mergepath_partition(s_key_merged_l1, merged_size_l1,
                                     &s_key_merged_l1[merged_size_l1], reg_reuse1, diag);

            mergepath_serialmerge(s_key_merged_l1, s_val_merged_l1,
                                  mp, merged_size_l1, merged_size_l1 + diag - mp, merged_size_l1 + reg_reuse1,
                                  reg_key, reg_val, VT);
            barrier(CLK_LOCAL_MEM_FENCE);

            for (is = 0; is < VT; is++)
            {
                s_key_merged_l1[diag + is] = reg_key[is];
                s_val_merged_l1[diag + is] = reg_val[is];
            }
            barrier(CLK_LOCAL_MEM_FENCE);

            merged_size_l1 += reg_reuse1; // reg_reuse1 = size_b = s_scan[local_size];
            start_col_index_B += local_size;
        }

        start_col_index_A++;
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    if (local_id == 0)
    {
        d_csrRowPtrC[row_id] = merged_size_l2 + merged_size_l1;
        d_queue[queue_id + 2] = -1;
    }

    // dump l1 to global
    readwrite_mergedlist(d_key_merged, d_val_merged, s_key_merged_l1, s_val_merged_l1,
                         merged_size_l1, merged_size_l2, 1);
    barrier(CLK_GLOBAL_MEM_FENCE);

    // merge l2 + l1 on global
    VT = ceil((merged_size_l2 + merged_size_l1) / local_size_float);
    diag = VT * local_id;
    mp = mergepath_partition_global(d_key_merged, merged_size_l2,
                                    &d_key_merged[merged_size_l2], merged_size_l1, diag);

    mergepath_serialmerge_global(d_key_merged, d_val_merged,
                          mp, merged_size_l2, merged_size_l2 + diag - mp, merged_size_l2 + merged_size_l1,
                          reg_key, reg_val, VT);
    barrier(CLK_GLOBAL_MEM_FENCE);

    for (is = 0; is < VT; is++)
    {
        d_key_merged[diag + is] = reg_key[is];
        d_val_merged[diag + is] = reg_val[is];
    }
}


removing old file

removing old file

// http://stackoverflow.com/questions/2755750/diamond-square-algorithm

const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;

__kernel void diamondsquare(__read_only image2d_t random_values, __write_only image2d_t heightmap, __global float* frequency, __global float* persistence, __global int* octaves)
{
    int2 dimensions = get_image_dim(heightmap);
    int width = dimensions.x;
    int height = dimensions.y;
    int data_size = width+1;
    float seed = 1000;

    int2 top_left = (int2)(0, 0);
    int2 top_right = (int2)(width-1, 0);
    int2 bottom_left = (int2)(0, height-1);
    int2 bottom_right = (int2)(width-1, height-1);

    float4 seedvalue;
    seedvalue.x = seed;
    seedvalue.y = seed;
    seedvalue.z = seed;
    seedvalue.w = seed;

    write_imagef(heightmap, top_left, seedvalue);
    write_imagef(heightmap, top_right, seedvalue);
    write_imagef(heightmap, bottom_left, seedvalue);
    write_imagef(heightmap, bottom_right, seedvalue);

    float h = seed / 2;
    for (int sideLength = data_size-1; sideLength >= 2; sideLength /= 2, h /= 2.0)
    {
        int halfSide = sideLength/2;
        for (int x = 0; x < data_size-1; x += sideLength)
        {
            for (int y = 0; y < data_size-1; y += sideLength)
            {

            }
        }
    }
}


Fix gamera generation offset for CMJ sampler

/**********************************************************************
Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
********************************************************************/
#ifndef SAMPLING_CL
#define SAMPLING_CL

#include <../Baikal/Kernels/CL/utils.cl>

#define SAMPLE_DIMS_PER_BOUNCE 300
#define SAMPLE_DIM_CAMERA_OFFSET 1
#define SAMPLE_DIM_SURFACE_OFFSET 5
#define SAMPLE_DIM_VOLUME_APPLY_OFFSET 101
#define SAMPLE_DIM_VOLUME_EVALUATE_OFFSET 201
#define SAMPLE_DIM_IMG_PLANE_EVALUATE_OFFSET 401

typedef struct
{
    uint seq;
    uint s0;
    uint s1;
    uint s2;
} SobolSampler;

typedef struct _Sampler
{
    uint index;
    uint dimension;
    uint scramble;
    uint padding;
} Sampler;

#if SAMPLER == SOBOL
#define SAMPLER_ARG_LIST __global uint const* sobol_mat
#define SAMPLER_ARGS sobol_mat
#elif SAMPLER == RANDOM
#define SAMPLER_ARG_LIST int unused
#define SAMPLER_ARGS 0
#elif SAMPLER == CMJ
#define SAMPLER_ARG_LIST int unused
#define SAMPLER_ARGS 0
#endif

/**
    Sobol sampler
**/
#define MATSIZE 52

// The code is taken from: http://gruenschloss.org/sobol/kuo-2d-proj-single-precision.zip
//
float SobolSampler_Sample1D(Sampler* sampler, __global uint const* mat)
{
    uint result = sampler->scramble;
    uint index = sampler->index;
    for (uint i = sampler->dimension * MATSIZE; index;  index >>= 1, ++i)
    {
        if (index & 1)
            result ^= mat[i];
    }

    return result * (1.f / (1UL << 32));
}

/**
    Random sampler
**/

/// Hash function
uint WangHash(uint seed)
{
    seed = (seed ^ 61) ^ (seed >> 16);
    seed *= 9;
    seed = seed ^ (seed >> 4);
    seed *= 0x27d4eb2d;
    seed = seed ^ (seed >> 15);
    return seed;
}

/// Return random unsigned
uint UniformSampler_SampleUint(Sampler* sampler)
{
    sampler->index = WangHash(1664525U * sampler->index + 1013904223U);
    return sampler->index;
}

/// Return random float
float UniformSampler_Sample1D(Sampler* sampler)
{
    return ((float)UniformSampler_SampleUint(sampler)) / 0xffffffffU;
}

/**
    Correllated multi-jittered
**/

uint permute(uint i, uint l, uint p)
{
    unsigned w = l - 1;
    w |= w >> 1;
    w |= w >> 2;
    w |= w >> 4;
    w |= w >> 8;
    w |= w >> 16;

    do
    {
        i ^= p;
        i *= 0xe170893d;
        i ^= p >> 16;
        i ^= (i & w) >> 4;
        i ^= p >> 8;
        i *= 0x0929eb3f;
        i ^= p >> 23;
        i ^= (i & w) >> 1;
        i *= 1 | p >> 27;
        i *= 0x6935fa69;
        i ^= (i & w) >> 11;
        i *= 0x74dcb303;
        i ^= (i & w) >> 2;
        i *= 0x9e501cc3;
        i ^= (i & w) >> 2;
        i *= 0xc860a3df;
        i &= w;
        i ^= i >> 5;
    } while (i >= l);
    return (i + p) % l;
}

float randfloat(uint i, uint p)
{
    i ^= p;
    i ^= i >> 17;
    i ^= i >> 10;
    i *= 0xb36534e5;
    i ^= i >> 12;
    i ^= i >> 21;
    i *= 0x93fc4795;
    i ^= 0xdf6e307f;
    i ^= i >> 17;
    i *= 1 | p >> 18;
    return i * (1.0f / 4294967808.0f);
}

float2 cmj(int s, int n, int p)
{
    int sx = permute(s % n, n, p * 0xa511e9b3);
    int sy = permute(s / n, n, p * 0x63d83595);
    float jx = randfloat(s, p * 0xa399d265);
    float jy = randfloat(s, p * 0x711ad6a5);

    return make_float2((s % n + (sy + jx) / n) / n,
        (s / n + (sx + jy) / n) / n);
}

float2 CmjSampler_Sample2D(Sampler* sampler)
{
    int idx = permute(sampler->index, CMJ_DIM * CMJ_DIM, 0xa399d265 * sampler->dimension * sampler->scramble);
    return cmj(idx, CMJ_DIM, sampler->dimension * sampler->scramble);
}

#if SAMPLER == SOBOL
void Sampler_Init(Sampler* sampler, uint index, uint start_dimension, uint scramble)
{
    sampler->index = index;
    sampler->scramble = scramble;
    sampler->dimension = start_dimension;
}
#elif SAMPLER == RANDOM
void Sampler_Init(Sampler* sampler, uint seed)
{
    sampler->index = seed;
    sampler->scramble = 0;
    sampler->dimension = 0;
}
#elif SAMPLER == CMJ
void Sampler_Init(Sampler* sampler, uint index, uint dimension, uint scramble)
{
    sampler->index = index;
    sampler->scramble = scramble;
    sampler->dimension = dimension;
}
#endif

float2 Sampler_Sample2D(Sampler* sampler, SAMPLER_ARG_LIST)
{
#if SAMPLER == SOBOL
    float2 sample;
    sample.x = SobolSampler_Sample1D(sampler, SAMPLER_ARGS);
    ++(sampler->dimension);
    sample.y = SobolSampler_Sample1D(sampler, SAMPLER_ARGS);
    ++(sampler->dimension);
    return sample;
#elif SAMPLER == RANDOM
    float2 sample;
    sample.x = UniformSampler_Sample1D(sampler);
    sample.y = UniformSampler_Sample1D(sampler);
    return sample;
#elif SAMPLER == CMJ
    float2 sample;
    sample = CmjSampler_Sample2D(sampler);
    ++(sampler->dimension);
    return sample;
#endif
}

float Sampler_Sample1D(Sampler* sampler, SAMPLER_ARG_LIST)
{
#if SAMPLER == SOBOL
    float sample = SobolSampler_Sample1D(sampler, SAMPLER_ARGS);
    ++(sampler->dimension);
    return sample;
#elif SAMPLER == RANDOM
    return UniformSampler_Sample1D(sampler);
#elif SAMPLER == CMJ
    float2 sample;
    sample = CmjSampler_Sample2D(sampler);
    ++(sampler->dimension);
    return sample.x;
#endif
}

/// Sample hemisphere with cos weight
float3 Sample_MapToHemisphere(
                        // Sample
                        float2 sample,
                        // Hemisphere normal
                        float3 n,
                        // Cos power
                        float e
                        )
{
    // Construct basis
    float3 u = GetOrthoVector(n);
    float3 v = cross(u, n);
    u = cross(n, v);

    // Calculate 2D sample
    float r1 = sample.x;
    float r2 = sample.y;

    // Transform to spherical coordinates
    float sinpsi = sin(2*PI*r1);
    float cospsi = cos(2*PI*r1);
    float costheta = pow(1.f - r2, 1.f/(e + 1.f));
    float sintheta = sqrt(1.f - costheta * costheta);

    // Return the result
    return normalize(u * sintheta * cospsi + v * sintheta * sinpsi + n * costheta);
}

float2 Sample_MapToDisk(
    // Sample
    float2 sample
    )
{
    float r = native_sqrt(sample.x);
    float theta = 2 * PI * sample.y;
    return make_float2(r * native_cos(theta), r * native_sin(theta));
}

float2 Sample_MapToDiskConcentric(
    // Sample
    float2 sample
    )
{
    float2 offset = 2.f * sample - make_float2(1.f, 1.f);

    if (offset.x == 0 && offset.y == 0) return 0.f;

    float theta, r;

    if (fabs(offset.x) > fabs(offset.y))
    {
        r = offset.x;
        theta = PI / 4.f * (offset.y / offset.x);
    }
    else
    {
        r = offset.y;
        theta = PI / 2.f * ( 1.f - 0.5f * (offset.x / offset.y));
    }

    return make_float2(r * native_cos(theta), r * native_sin(theta));
}

/// Sample hemisphere with cos weight
float3 Sample_MapToSphere(
                        // Sample
                        float2 sample
                        )
{
    float z = 1.f - 2.f * sample.x;
    float r = native_sqrt(max(0.f, 1.f - z*z));
    float phi = 2.f * PI * sample.y;
    float x = cos(phi);
    float y = sin(phi);

    // Return the result
    return make_float3(x,y,z);
}

float2 Sample_MapToPolygon(int n, float2 sample, float sample1)
{
    float theta = 2.f * PI / n;
    int edge = clamp((int)(sample1 * n), 0, n - 1);
    float t = native_sqrt(sample.x);
    float u = 1.f - t;
    float v = t * sample.y;
    float2 v1 = make_float2(native_cos(theta * edge), native_sin(theta * edge));
    float2 v2 = make_float2(native_cos(theta * (edge + 1)), native_sin(theta * (edge + 1)));
    return u*v1 + v*v2;;
}

/// Power heuristic for multiple importance sampling
float PowerHeuristic(int nf, float fpdf, int ng, float gpdf)
{
    float f = nf * fpdf;
    float g = ng * gpdf;
    return (f*f) / (f*f + g*g);
}

/// Balance heuristic for multiple importance sampling
float BalanceHeuristic(int nf, float fpdf, int ng, float gpdf)
{
    float f = nf * fpdf;
    float g = ng * gpdf;
    return (f) / (f + g);
}

int lower_bound(GLOBAL float const* values, int n, float value)
{
    int count = n;
    int b = 0;
    int it = 0;
    int step = 0;

    while (count > 0)
    {
        it = b;
        step = count / 2;
        it += step;
        if (values[it] < value)
        {
            b = ++it;
            count -= step + 1;
        }
        else
        {
            count = step;
        }
    }

    return b;
}
/// Sample 1D distribution
float Distribution1D_Sample(float s, GLOBAL int const* data, float* pdf)
{
    int num_segments = data[0];

    GLOBAL float const* cdf_data = (GLOBAL float const*)&data[1];
    GLOBAL float const* pdf_data = cdf_data + num_segments + 1;

    int segment_idx = max(lower_bound(cdf_data, num_segments + 1, s), 1);

    // Find lerp coefficient
    float du = (s - cdf_data[segment_idx - 1]) / (cdf_data[segment_idx] - cdf_data[segment_idx - 1]);

    // Calc pdf
    *pdf = pdf_data[segment_idx - 1];

    return (segment_idx - 1 + du) / num_segments;;
}

/// Sample 1D distribution
int Distribution1D_SampleDiscrete(float s, GLOBAL int const* data, float* pdf)
{
    int num_segments = data[0];

    GLOBAL float const* cdf_data = (GLOBAL float const*)&data[1];
    GLOBAL float const* pdf_data = cdf_data + num_segments + 1;

    int segment_idx = max(lower_bound(cdf_data, num_segments + 1, s), 1);

    // Find lerp coefficient
    float du = (s - cdf_data[segment_idx - 1]) / (cdf_data[segment_idx] - cdf_data[segment_idx - 1]);

    // Calc pdf
    *pdf = pdf_data[segment_idx - 1] / num_segments;

    return segment_idx - 1;
}

/// PDF of  1D distribution
float Distribution1D_GetPdf(float s, GLOBAL int const* data)
{
    int num_segments = data[0];
    GLOBAL float const* cdf_data = (GLOBAL float const*)&data[1];
    GLOBAL float const* pdf_data = cdf_data + num_segments + 1;

    int segment_idx = max(lower_bound(cdf_data, num_segments + 1, s), 1);

    // Calc pdf
    return pdf_data[segment_idx - 1];
}

/// PDF of  1D distribution
float Distribution1D_GetPdfDiscreet(int d, GLOBAL int const* data)
{
    int num_segments = data[0];
    GLOBAL float const* cdf_data = (GLOBAL float const*)&data[1];
    GLOBAL float const* pdf_data = cdf_data + num_segments + 1;

    // Calc pdf
    return pdf_data[d] / num_segments;
}

#endif // SAMPLING_CL

/**********************************************************************
Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
********************************************************************/
#ifndef SAMPLING_CL
#define SAMPLING_CL

#include <../Baikal/Kernels/CL/utils.cl>

#define SAMPLE_DIMS_PER_BOUNCE 300
#define SAMPLE_DIM_CAMERA_OFFSET 0
#define SAMPLE_DIM_SURFACE_OFFSET 4
#define SAMPLE_DIM_VOLUME_APPLY_OFFSET 100
#define SAMPLE_DIM_VOLUME_EVALUATE_OFFSET 200
#define SAMPLE_DIM_IMG_PLANE_EVALUATE_OFFSET 200

typedef struct
{
    uint seq;
    uint s0;
    uint s1;
    uint s2;
} SobolSampler;

typedef struct _Sampler
{
    uint index;
    uint dimension;
    uint scramble;
    uint padding;
} Sampler;

#if SAMPLER == SOBOL
#define SAMPLER_ARG_LIST __global uint const* sobol_mat
#define SAMPLER_ARGS sobol_mat
#elif SAMPLER == RANDOM
#define SAMPLER_ARG_LIST int unused
#define SAMPLER_ARGS 0
#elif SAMPLER == CMJ
#define SAMPLER_ARG_LIST int unused
#define SAMPLER_ARGS 0
#endif

/**
    Sobol sampler
**/
#define MATSIZE 52

// The code is taken from: http://gruenschloss.org/sobol/kuo-2d-proj-single-precision.zip
//
float SobolSampler_Sample1D(Sampler* sampler, __global uint const* mat)
{
    uint result = sampler->scramble;
    uint index = sampler->index;
    for (uint i = sampler->dimension * MATSIZE; index;  index >>= 1, ++i)
    {
        if (index & 1)
            result ^= mat[i];
    }

    return result * (1.f / (1UL << 32));
}

/**
    Random sampler
**/

/// Hash function
uint WangHash(uint seed)
{
    seed = (seed ^ 61) ^ (seed >> 16);
    seed *= 9;
    seed = seed ^ (seed >> 4);
    seed *= 0x27d4eb2d;
    seed = seed ^ (seed >> 15);
    return seed;
}

/// Return random unsigned
uint UniformSampler_SampleUint(Sampler* sampler)
{
    sampler->index = WangHash(1664525U * sampler->index + 1013904223U);
    return sampler->index;
}

/// Return random float
float UniformSampler_Sample1D(Sampler* sampler)
{
    return ((float)UniformSampler_SampleUint(sampler)) / 0xffffffffU;
}

/**
    Correllated multi-jittered
**/

uint permute(uint i, uint l, uint p)
{
    unsigned w = l - 1;
    w |= w >> 1;
    w |= w >> 2;
    w |= w >> 4;
    w |= w >> 8;
    w |= w >> 16;

    do
    {
        i ^= p;
        i *= 0xe170893d;
        i ^= p >> 16;
        i ^= (i & w) >> 4;
        i ^= p >> 8;
        i *= 0x0929eb3f;
        i ^= p >> 23;
        i ^= (i & w) >> 1;
        i *= 1 | p >> 27;
        i *= 0x6935fa69;
        i ^= (i & w) >> 11;
        i *= 0x74dcb303;
        i ^= (i & w) >> 2;
        i *= 0x9e501cc3;
        i ^= (i & w) >> 2;
        i *= 0xc860a3df;
        i &= w;
        i ^= i >> 5;
    } while (i >= l);
    return (i + p) % l;
}

float randfloat(uint i, uint p)
{
    i ^= p;
    i ^= i >> 17;
    i ^= i >> 10;
    i *= 0xb36534e5;
    i ^= i >> 12;
    i ^= i >> 21;
    i *= 0x93fc4795;
    i ^= 0xdf6e307f;
    i ^= i >> 17;
    i *= 1 | p >> 18;
    return i * (1.0f / 4294967808.0f);
}

float2 cmj(int s, int n, int p)
{
    int sx = permute(s % n, n, p * 0xa511e9b3);
    int sy = permute(s / n, n, p * 0x63d83595);
    float jx = randfloat(s, p * 0xa399d265);
    float jy = randfloat(s, p * 0x711ad6a5);

    return make_float2((s % n + (sy + jx) / n) / n,
        (s / n + (sx + jy) / n) / n);
}

float2 CmjSampler_Sample2D(Sampler* sampler)
{
    int idx = permute(sampler->index, CMJ_DIM * CMJ_DIM, 0xa399d265 * sampler->dimension * sampler->scramble);
    return cmj(idx, CMJ_DIM, sampler->dimension * sampler->scramble);
}

#if SAMPLER == SOBOL
void Sampler_Init(Sampler* sampler, uint index, uint start_dimension, uint scramble)
{
    sampler->index = index;
    sampler->scramble = scramble;
    sampler->dimension = start_dimension;
}
#elif SAMPLER == RANDOM
void Sampler_Init(Sampler* sampler, uint seed)
{
    sampler->index = seed;
    sampler->scramble = 0;
    sampler->dimension = 0;
}
#elif SAMPLER == CMJ
void Sampler_Init(Sampler* sampler, uint index, uint dimension, uint scramble)
{
    sampler->index = index;
    sampler->scramble = scramble;
    sampler->dimension = dimension;
}
#endif

float2 Sampler_Sample2D(Sampler* sampler, SAMPLER_ARG_LIST)
{
#if SAMPLER == SOBOL
    float2 sample;
    sample.x = SobolSampler_Sample1D(sampler, SAMPLER_ARGS);
    ++(sampler->dimension);
    sample.y = SobolSampler_Sample1D(sampler, SAMPLER_ARGS);
    ++(sampler->dimension);
    return sample;
#elif SAMPLER == RANDOM
    float2 sample;
    sample.x = UniformSampler_Sample1D(sampler);
    sample.y = UniformSampler_Sample1D(sampler);
    return sample;
#elif SAMPLER == CMJ
    float2 sample;
    sample = CmjSampler_Sample2D(sampler);
    ++(sampler->dimension);
    return sample;
#endif
}

float Sampler_Sample1D(Sampler* sampler, SAMPLER_ARG_LIST)
{
#if SAMPLER == SOBOL
    float sample = SobolSampler_Sample1D(sampler, SAMPLER_ARGS);
    ++(sampler->dimension);
    return sample;
#elif SAMPLER == RANDOM
    return UniformSampler_Sample1D(sampler);
#elif SAMPLER == CMJ
    float2 sample;
    sample = CmjSampler_Sample2D(sampler);
    ++(sampler->dimension);
    return sample.x;
#endif
}

/// Sample hemisphere with cos weight
float3 Sample_MapToHemisphere(
                        // Sample
                        float2 sample,
                        // Hemisphere normal
                        float3 n,
                        // Cos power
                        float e
                        )
{
    // Construct basis
    float3 u = GetOrthoVector(n);
    float3 v = cross(u, n);
    u = cross(n, v);

    // Calculate 2D sample
    float r1 = sample.x;
    float r2 = sample.y;

    // Transform to spherical coordinates
    float sinpsi = sin(2*PI*r1);
    float cospsi = cos(2*PI*r1);
    float costheta = pow(1.f - r2, 1.f/(e + 1.f));
    float sintheta = sqrt(1.f - costheta * costheta);

    // Return the result
    return normalize(u * sintheta * cospsi + v * sintheta * sinpsi + n * costheta);
}

float2 Sample_MapToDisk(
    // Sample
    float2 sample
    )
{
    float r = native_sqrt(sample.x);
    float theta = 2 * PI * sample.y;
    return make_float2(r * native_cos(theta), r * native_sin(theta));
}

float2 Sample_MapToDiskConcentric(
    // Sample
    float2 sample
    )
{
    float2 offset = 2.f * sample - make_float2(1.f, 1.f);

    if (offset.x == 0 && offset.y == 0) return 0.f;

    float theta, r;

    if (fabs(offset.x) > fabs(offset.y))
    {
        r = offset.x;
        theta = PI / 4.f * (offset.y / offset.x);
    }
    else
    {
        r = offset.y;
        theta = PI / 2.f * ( 1.f - 0.5f * (offset.x / offset.y));
    }

    return make_float2(r * native_cos(theta), r * native_sin(theta));
}

/// Sample hemisphere with cos weight
float3 Sample_MapToSphere(
                        // Sample
                        float2 sample
                        )
{
    float z = 1.f - 2.f * sample.x;
    float r = native_sqrt(max(0.f, 1.f - z*z));
    float phi = 2.f * PI * sample.y;
    float x = cos(phi);
    float y = sin(phi);

    // Return the result
    return make_float3(x,y,z);
}

float2 Sample_MapToPolygon(int n, float2 sample, float sample1)
{
    float theta = 2.f * PI / n;
    int edge = clamp((int)(sample1 * n), 0, n - 1);
    float t = native_sqrt(sample.x);
    float u = 1.f - t;
    float v = t * sample.y;
    float2 v1 = make_float2(native_cos(theta * edge), native_sin(theta * edge));
    float2 v2 = make_float2(native_cos(theta * (edge + 1)), native_sin(theta * (edge + 1)));
    return u*v1 + v*v2;;
}

/// Power heuristic for multiple importance sampling
float PowerHeuristic(int nf, float fpdf, int ng, float gpdf)
{
    float f = nf * fpdf;
    float g = ng * gpdf;
    return (f*f) / (f*f + g*g);
}

/// Balance heuristic for multiple importance sampling
float BalanceHeuristic(int nf, float fpdf, int ng, float gpdf)
{
    float f = nf * fpdf;
    float g = ng * gpdf;
    return (f) / (f + g);
}

int lower_bound(GLOBAL float const* values, int n, float value)
{
    int count = n;
    int b = 0;
    int it = 0;
    int step = 0;

    while (count > 0)
    {
        it = b;
        step = count / 2;
        it += step;
        if (values[it] < value)
        {
            b = ++it;
            count -= step + 1;
        }
        else
        {
            count = step;
        }
    }

    return b;
}
/// Sample 1D distribution
float Distribution1D_Sample(float s, GLOBAL int const* data, float* pdf)
{
    int num_segments = data[0];

    GLOBAL float const* cdf_data = (GLOBAL float const*)&data[1];
    GLOBAL float const* pdf_data = cdf_data + num_segments + 1;

    int segment_idx = max(lower_bound(cdf_data, num_segments + 1, s), 1);

    // Find lerp coefficient
    float du = (s - cdf_data[segment_idx - 1]) / (cdf_data[segment_idx] - cdf_data[segment_idx - 1]);

    // Calc pdf
    *pdf = pdf_data[segment_idx - 1];

    return (segment_idx - 1 + du) / num_segments;;
}

/// Sample 1D distribution
int Distribution1D_SampleDiscrete(float s, GLOBAL int const* data, float* pdf)
{
    int num_segments = data[0];

    GLOBAL float const* cdf_data = (GLOBAL float const*)&data[1];
    GLOBAL float const* pdf_data = cdf_data + num_segments + 1;

    int segment_idx = max(lower_bound(cdf_data, num_segments + 1, s), 1);

    // Find lerp coefficient
    float du = (s - cdf_data[segment_idx - 1]) / (cdf_data[segment_idx] - cdf_data[segment_idx - 1]);

    // Calc pdf
    *pdf = pdf_data[segment_idx - 1] / num_segments;

    return segment_idx - 1;
}

/// PDF of  1D distribution
float Distribution1D_GetPdf(float s, GLOBAL int const* data)
{
    int num_segments = data[0];
    GLOBAL float const* cdf_data = (GLOBAL float const*)&data[1];
    GLOBAL float const* pdf_data = cdf_data + num_segments + 1;

    int segment_idx = max(lower_bound(cdf_data, num_segments + 1, s), 1);

    // Calc pdf
    return pdf_data[segment_idx - 1];
}

/// PDF of  1D distribution
float Distribution1D_GetPdfDiscreet(int d, GLOBAL int const* data)
{
    int num_segments = data[0];
    GLOBAL float const* cdf_data = (GLOBAL float const*)&data[1];
    GLOBAL float const* pdf_data = cdf_data + num_segments + 1;

    // Calc pdf
    return pdf_data[d] / num_segments;
}

#endif // SAMPLING_CL


working on wind zones

working on wind zones

const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;

float Cosine_Interpolate(float a, float b, float x)
{
	float ft = x * 3.1415927f;
	float f = (1.0f - cos(ft)) * 0.5f;

	return  a * ( 1 - f ) + b * f;
}

// Some crazy mathematical function I found on the interwebs
float Noise(int x, int y)
{
	int n = x + y * 57;
	n = ( n << 13 ) ^ n;
	return ( 1.f - ( ( n * ( n * n * 15731 + 789221 ) + 1376312589 ) & 0x7fffffff ) / 1073741824.0 );
}

// Random data from an initialized image
float4 imageNoise(int x, int y, __read_only image2d_t random_values)
{
    int2 coords = (int2)(x, y);
    int2 s = get_image_dim(random_values);
    if (coords.x < 0)
        coords.x = 0;
    else if (coords.x >= s.x)
        coords.x = s.x - 1;
    if (coords.y < 0)
        coords.y = 0;
    else if (coords.y >= s.y)
        coords.y = s.y - 1;

    return read_imagef(random_values, sampler, coords);
}

float SmoothedNoise1(float x, float y, __read_only image2d_t random_values)
{
	float corners = ( imageNoise(x-1, y-1, random_values).x+imageNoise(x+1, y-1, random_values).x+imageNoise(x-1, y+1, random_values).x+imageNoise(x+1, y+1, random_values).x ) / 16.0f;
	float sides   = ( imageNoise(x-1, y, random_values).x  +imageNoise(x+1, y, random_values).x  +imageNoise(x, y-1, random_values).x  +imageNoise(x, y+1, random_values) ).x /  8.0f;
	float center  =  imageNoise(x, y, random_values).x / 4.0f;
	return corners + sides + center;
}

float InterpolatedNoise1(float x, float y, __read_only image2d_t random_values)
{
	int integer_X    = (int)floor(x);
	float fractional_X = fabs (x - integer_X);

	int integer_Y    = (int)floor(y);
	float fractional_Y = fabs(y - integer_Y);

	float v1 = SmoothedNoise1(integer_X,     integer_Y, random_values);
	float v2 = SmoothedNoise1(integer_X + 1, integer_Y, random_values);
	float v3 = SmoothedNoise1(integer_X,     integer_Y + 1, random_values);
	float v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1, random_values);

	float i1 = Cosine_Interpolate(v1 , v2 , fractional_X);
	float i2 = Cosine_Interpolate(v3 , v4 , fractional_X);

	return Cosine_Interpolate(i1 , i2 , fractional_Y);
}

float GetPerlin( float x, float y, float frequency, float persistence, int octaves, __read_only image2d_t random_values)
{
	float total = 0.0f;
	float p = persistence;
	float freq = frequency;
	int n = octaves -1;

	for( int i = 0; i < n; ++i )
	{
		float frequency2 = pow(freq, (float)i);
		float amplitude = pow(p, (float)i);

		total = total + InterpolatedNoise1(x * frequency2, y * frequency2, random_values) * amplitude;
	}

	return total;
}

float linear_interpolate(float x0, float x1, float alpha)
{
    if (alpha > 1.0)
        alpha = 1.0;
    else if (alpha <= 0)
        alpha = 0.0;
    float interpolation = x0 * (1.0 - alpha) + alpha * x1;

    return interpolation;
}

// Calculates the direction of wind based on 7 wind zones
// This is performed by first checking in which area our desired coordinate is,
// and then performing a linear interpolation between the two ends of that wind area
// Wind directions are described by radians
float getLatitudeWinddir(float coord_y)
{
    // North pole: south, 3.14159 rads
    // North circle: north, 0 rads
    // North tropic: east, 1.570795 rads
    // Equator: west 4.712385 rads
    // South tropic: east,  1.570795 rads
    // South circle: south, 3.14159 rads
    // South pole: north, 0 rads

    // north pole: +90 degrees
    // north circle: +66.33 degrees
    // north tropic: +23.27 degrees
    // equator: 0 degrees
    // south tropic: -23.27 degrees
    // south circle: -66.33 degrees
    // south pole: -90 degrees

    // Convert the height coordinate from (0...1) to the range of -90...90
    float latitude = linear_interpolate(90, -90, coord_y);

    if (latitude > 66 && latitude < 67)
        return 100;
    if (latitude > 23 && latitude < 24)
        return 100;
    if (latitude > 0 && latitude < 1)
        return 100;
    if (latitude <  -23 && latitude > -24)
        return 100;
    if (latitude <  -66 && latitude > -67)
        return 100;

    // For each latitude range, calculate influence of that wind region
    if (latitude >= 66.33)
    {
        // North pole - north cicle
        // First figure out relative position to the end points of this region (0...1)
        float latitude_influence = (90.0 - latitude) / 23.67; // 90 - 63.66
        // Then calculate the total wind direction by using linear interpolation between the two wind points
        return linear_interpolate(3.14159, 0, latitude_influence);
    }
    else if (latitude < 66.33 && latitude >= 23.27)
    {
        // North circle - north tropic
        float latitude_influence = (63.33 - latitude) / 43.06; // 66.33 - 23.27
        return linear_interpolate(0, 1.570795, latitude_influence);
    }
    else if (latitude < 23.27 && latitude >= 0)
    {
        // North tropic - equator
        float latitude_influence = (23.27 - latitude) / 23.27; // 23.27 - 0
        return linear_interpolate(1.570795, 4.712385, latitude_influence);
    }
    else if (latitude < 0 && latitude >= -23.27)
    {
        // Equator - south tropic
        float latitude_influence = latitude / -23.27; // 23.27 - 0
        return linear_interpolate(4.712385, 1.570795, latitude_influence);
    }
    else if (latitude < -23.27 && latitude >= -66.33)
    {
        // South tropic - south circle
        float latitude_influence = (-22.27 - latitude) / 43.06; // 66.33 - 23.27
        return linear_interpolate(1.570795, 3.14159, latitude_influence);
    }
    else
    {
        // South circle - south pole
        float latitude_influence = (-66.33 - latitude) / 23.67; // 90 - 63.66;
        return linear_interpolate(3.14159, 0, latitude_influence);
    }

}

__kernel void winddirection(__read_only image2d_t random_values,  __write_only image2d_t windmap)
{
    // Get the (x,y) coordinates of our desired point
    int x = get_global_id(0);
	int y = get_global_id(1);

	int2 coord = (int2) (x, y);
    int2 s = get_image_dim(windmap);

    float frequency = 0.06;
    float persistence = 1.7;
    int octaves = 8;

	float perlin = GetPerlin(x, y, frequency, persistence, octaves, random_values);

    float angle_rads = 0.0;

    // Convert y coordinate to 0...1 range
    float coord_y = coord.y / (float)s.y;

    float interpolated_direction = getLatitudeWinddir(coord_y);

    float t = interpolated_direction;

    float4 outcol;
    outcol.x = 0;
    outcol.y = 0;
    outcol.z = 0;
    outcol.w = 0;

    t *= perlin;

    // North pole: south, 3.14159 rads
    // North circle: north, 0 rads
    // North tropic: east, 1.570795 rads
    // Equator: west 4.712385 rads
    // South tropic: east,  1.570795 rads
    // South circle: south, 3.14159 rads
    // South pole: north, 0 rads

    if (t == 100)
    {
        outcol.x = 50;
        outcol.y = 255;
        outcol.z = 150;
    }
    else if (t < 1.570795)
        outcol.x = linear_interpolate(0.5, 1.0, t / 1.570795);
    else if (t > 1.570795 && t < 3.14159)
        outcol.y = linear_interpolate(0.5, 1.0, (3.14159 - t) / 1.570795);
    else if (t > 3.14159 && t < 4.712385)
        outcol.z = linear_interpolate(0.5, 1.0, (4.712385 - t) / 1.570795);
    else if (t > 4.712385 && t < 6)
    {
        outcol.x = 0.5;
        outcol.y = 0.5,
        outcol.z = 0.5;
    }
    else
    {

        outcol.x = 0.0;
        outcol.y = 0.0,
        outcol.z = 0.0;
    }

	write_imagef(windmap, coord, outcol);
}

const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;

float Cosine_Interpolate(float a, float b, float x)
{
	float ft = x * 3.1415927f;
	float f = (1.0f - cos(ft)) * 0.5f;

	return  a * ( 1 - f ) + b * f;
}

// Some crazy mathematical function I found on the interwebs
float Noise(int x, int y)
{
	int n = x + y * 57;
	n = ( n << 13 ) ^ n;
	return ( 1.f - ( ( n * ( n * n * 15731 + 789221 ) + 1376312589 ) & 0x7fffffff ) / 1073741824.0 );
}

// Random data from an initialized image
float4 imageNoise(int x, int y, __read_only image2d_t random_values)
{
    int2 coords = (int2)(x, y);
    int2 s = get_image_dim(random_values);
    if (coords.x < 0)
        coords.x = 0;
    else if (coords.x >= s.x)
        coords.x = s.x - 1;
    if (coords.y < 0)
        coords.y = 0;
    else if (coords.y >= s.y)
        coords.y = s.y - 1;

    return read_imagef(random_values, sampler, coords);
}

float SmoothedNoise1(float x, float y, __read_only image2d_t random_values)
{
	float corners = ( imageNoise(x-1, y-1, random_values).x+imageNoise(x+1, y-1, random_values).x+imageNoise(x-1, y+1, random_values).x+imageNoise(x+1, y+1, random_values).x ) / 16.0f;
	float sides   = ( imageNoise(x-1, y, random_values).x  +imageNoise(x+1, y, random_values).x  +imageNoise(x, y-1, random_values).x  +imageNoise(x, y+1, random_values) ).x /  8.0f;
	float center  =  imageNoise(x, y, random_values).x / 4.0f;
	return corners + sides + center;
}

float InterpolatedNoise1(float x, float y, __read_only image2d_t random_values)
{
	int integer_X    = (int)floor(x);
	float fractional_X = fabs (x - integer_X);

	int integer_Y    = (int)floor(y);
	float fractional_Y = fabs(y - integer_Y);

	float v1 = SmoothedNoise1(integer_X,     integer_Y, random_values);
	float v2 = SmoothedNoise1(integer_X + 1, integer_Y, random_values);
	float v3 = SmoothedNoise1(integer_X,     integer_Y + 1, random_values);
	float v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1, random_values);

	float i1 = Cosine_Interpolate(v1 , v2 , fractional_X);
	float i2 = Cosine_Interpolate(v3 , v4 , fractional_X);

	return Cosine_Interpolate(i1 , i2 , fractional_Y);
}

float GetPerlin( float x, float y, float frequency, float persistence, int octaves, __read_only image2d_t random_values)
{
	float total = 0.0f;
	float p = persistence;
	float freq = frequency;
	int n = octaves -1;

	for( int i = 0; i < n; ++i )
	{
		float frequency2 = pow(freq, (float)i);
		float amplitude = pow(p, (float)i);

		total = total + InterpolatedNoise1(x * frequency2, y * frequency2, random_values) * amplitude;
	}

	return total;
}

float linear_interpolate(float x0, float x1, float alpha)
{
    if (alpha > 1.0)
        alpha = 1.0;
    else if (alpha <= 0)
        alpha = 0.0;
    float interpolation = x0 * (1.0 - alpha) + alpha * x1;

    return interpolation;
}

// Calculates the direction of wind based on 7 wind zones
// This is performed by first checking in which area our desired coordinate is,
// and then performing a linear interpolation between the two ends of that wind area
// Wind directions are described by radians
float getLatitudeWinddir(float coord_y)
{
    // North pole: south, 3.14159 rads
    // North circle: north, 0 rads
    // North tropic: east, 1.570795 rads
    // Equator: west 4.712385 rads
    // South tropic: east,  1.570795 rads
    // South circle: south, 3.14159 rads
    // South pole: north, 0 rads

    // north pole: +90 degrees
    // north circle: +66.33 degrees
    // north tropic: +23.27 degrees
    // equator: 0 degrees
    // south tropic: -23.27 degrees
    // south circle: -66.33 degrees
    // south pole: -90 degrees

    // Convert the height coordinate from (0...1) to the range of -90...90
    float latitude = linear_interpolate(90, -90, coord_y);

    // For each latitude range, calculate influence of that wind region
    if (latitude >= 66.33)
    {
        // North pole - north cicle
        // First figure out relative position to the end points of this region (0...1)
        float latitude_influence = (90.0 - latitude) / 23.67; // 90 - 63.66
        // Then calculate the total wind direction by using linear interpolation between the two wind points
        return linear_interpolate(3.14159, 0, latitude_influence);
    }
    else if (latitude < 66.33 && latitude >= 23.27)
    {
        // North circle - north tropic
        float latitude_influence = (63.33 - latitude) / 43.06; // 66.33 - 23.27
        return linear_interpolate(0, 1.570795, latitude_influence);
    }
    else if (latitude < 23.27 && latitude >= 0)
    {
        // North tropic - equator
        float latitude_influence = (23.27 - latitude) / 23.27; // 23.27 - 0
        return linear_interpolate(1.570795, 4.712385, latitude_influence);
    }
    else if (latitude < 0 && latitude >= -23.27)
    {
        // Equator - south tropic
        float latitude_influence = latitude / -23.27; // 23.27 - 0
        return linear_interpolate(4.712385, 1.570795, latitude_influence);
    }
    else if (latitude < -23.27 && latitude >= -66.33)
    {
        // South tropic - south circle
        float latitude_influence = (-22.27 - latitude) / 43.06; // 66.33 - 23.27
        return linear_interpolate(1.570795, 3.14159, latitude_influence);
    }
    else
    {
        // South circle - south pole
        float latitude_influence = (-66.33 - latitude) / 23.67; // 90 - 63.66;
        return linear_interpolate(3.14159, 0, latitude_influence);
    }

}

__kernel void winddirection(__read_only image2d_t random_values,  __write_only image2d_t windmap)
{
    // Get the (x,y) coordinates of our desired point
    int x = get_global_id(0);
	int y = get_global_id(1);

	int2 coord = (int2) (x, y);
    int2 s = get_image_dim(windmap);

    float frequency = 0.06;
    float persistence = 1.7;
    int octaves = 8;

	float perlin = GetPerlin(x, y, frequency, persistence, octaves, random_values);

    float angle_rads = 0.0;

    // Convert y coordinate to 0...1 range
    float coord_y = coord.y / (float)s.y;

    float interpolated_direction = getLatitudeWinddir(coord_y);

    float t = interpolated_direction;

    float4 outcol;
    outcol.x = 0;
    outcol.y = 0;
    outcol.z = 0;
    outcol.w = 0;

    // North pole: south, 3.14159 rads
    // North circle: north, 0 rads
    // North tropic: east, 1.570795 rads
    // Equator: west 4.712385 rads
    // South tropic: east,  1.570795 rads
    // South circle: south, 3.14159 rads
    // South pole: north, 0 rads

    if (t < 1.570795)
        outcol.x = 1.0;
    else if (t > 1.570795 && t < 3.14159)
        outcol.y = 1.0;
    else if (t > 3.14159 && t < 4.712385)
        outcol.z = 1.0;
    else
    {
        outcol.x = 1;
        outcol.y = 1,
        outcol.z = 1;
    }

	write_imagef(windmap, coord, outcol);
}


Give up raytracing box-by-box

Give up raytracing box-by-box

Numerical computation failure is frustrating

#pragma OPENCL EXTENSION CL_APPLE_gl_sharing : enable

const sampler_t sampler   = CLK_NORMALIZED_COORDS_FALSE |
                            CLK_ADDRESS_CLAMP_TO_EDGE |
                            CLK_FILTER_LINEAR;

int intersect_box(float3 origin,  float3 direction,
                  float3 box_min, float3 box_max,
                  float *t_near, float *t_far) {

    float3 direction_inv = (float3)(1.0f, 1.0f, 1.0f) / direction;
    float3 t_bottom      = direction_inv * (box_min - origin);
    float3 t_top         = direction_inv * (box_max - origin);

    float3 t_min = min(t_top, t_bottom);
    float3 t_max = max(t_top, t_bottom);

    float largest_t_min  = max(max(t_min.x, t_min.y),
                               max(t_min.x, t_min.z));
    float smallest_t_max = min(min(t_max.x, t_max.y),
                               min(t_max.x, t_max.z));

	*t_near = largest_t_min;
	*t_far  = smallest_t_max;

	return largest_t_min < smallest_t_max;
};

void kernel raytracing(__read_only  image3d_t volume,
                       __write_only image2d_t image,
                       float3 r1, float3 r2, float3 r3,
                       float3 camera, float2 f, float3 range) {
    float3 grid_size = 2.0f / range;
    int2 wh = (int2)(get_global_size(0), get_global_size(1));
    int2 uv = (int2)(get_global_id(0), get_global_id(1));

    // NDC   : Y upwards
    // Image : Y downwards
    float3 direction_camera = (float3)((2.0f * uv.x / (float)wh.x - 1.0f) / f.x,
                                       (1.0f - 2.0f * uv.y / (float)wh.y) / f.y,
                                       -1.0f);
    float3 direction_world = (float3)(dot(direction_camera, r1),
                                      dot(direction_camera, r2),
                                      dot(direction_camera, r3));
    direction_world.x = (fabs(direction_world.x) < 1e-10)
    ? 1e-10 : direction_world.x;
    direction_world.y = (fabs(direction_world.y) < 1e-10)
    ? 1e-10 : direction_world.y;
    direction_world.z = (fabs(direction_world.z) < 1e-10)
    ? 1e-10 : direction_world.z;

    float3 box_min = (float3)(-1.0f, -1.0f, -1.0f);
    float3 box_max = (float3)( 1.0f,  1.0f,  1.0f);

    float t_near, t_far;
    int hit = intersect_box(camera, direction_world,
                            box_min, box_max,
                            &t_near, &t_far);
    if (! hit) {
        write_imagef(image, uv, (float4)(0.0f, 0.0f, 0.0f, 1.0f));
        return;
    }

    t_near = (t_near < 0) ? 0 : t_near;
    float t = t_near;
    float4 color = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
    int max_iteration = (int)(range.x + range.y + range.z);
    float step_size   = 0.8f * grid_size.x;
    for (int i = 0; i < max_iteration; ++i) {
      float3 p = camera + t * direction_world;

      int4 volume_index = (int4)((int)((p.x + 1) / grid_size.x),
                                 (int)((p.y + 1) / grid_size.y),
                                 (int)((p.z + 1) / grid_size.z),
                                 1);

      color += read_imagef(volume, sampler, volume_index);
      t += step_size;
      if (t > t_far) break;
    }
    write_imagef(image, uv, 0.01f * (float4)(0.0f, color.y, 0.0f, 1.0f));
}

#pragma OPENCL EXTENSION CL_APPLE_gl_sharing : enable

#define max_iteration   1024

const sampler_t sampler   = CLK_NORMALIZED_COORDS_FALSE |
                            CLK_ADDRESS_CLAMP_TO_EDGE |
                            CLK_FILTER_LINEAR;

int intersect_box(float3 origin,  float3 direction,
                  float3 box_min, float3 box_max,
                  float *t_near, float *t_far) {

    float3 direction_inv = (float3)(1.0f, 1.0f, 1.0f) / direction;
    float3 t_bottom      = direction_inv * (box_min - origin);
    float3 t_top         = direction_inv * (box_max - origin);

    float3 t_min = min(t_top, t_bottom);
    float3 t_max = max(t_top, t_bottom);

    float largest_t_min  = max(max(t_min.x, t_min.y),
                               max(t_min.x, t_min.z));
    float smallest_t_max = min(min(t_max.x, t_max.y),
                               min(t_max.x, t_max.z));

	*t_near = largest_t_min;
	*t_far  = smallest_t_max;

	return largest_t_min < smallest_t_max;
};

void kernel raytracing(__read_only  image3d_t volume,
                       __write_only image2d_t image,
                       float3 r1, float3 r2, float3 r3,
                       float3 camera,
                       float2 f,
                       float3 range) {
    float step_size = 1.0f / range.x;
    range *= 0.5f;
    int2 wh = (int2)(get_global_size(0), get_global_size(1));
    int2 uv = (int2)(get_global_id(0), get_global_id(1));

    // NDC   : Y upwards
    // Image : Y downwards
    float3 direction_camera = (float3)((2.0f * uv.x / (float)wh.x - 1.0f) / f.x,
                                       (1.0f - 2.0f * uv.y / (float)wh.y) / f.y,
                                       -1.0f);
    float3 direction_world = (float3)(dot(direction_camera, r1),
                                      dot(direction_camera, r2),
                                      dot(direction_camera, r3));

    float3 box_min = (float3)(-1.0f, -1.0f, -1.0f);
    float3 box_max = (float3)( 1.0f,  1.0f,  1.0f);

    float t_near, t_far;
    int hit = intersect_box(camera, direction_world,
                            box_min, box_max,
                            &t_near, &t_far);
    if (! hit) {
        write_imagef(image, uv, (float4)(0.0f, 0.0f, 0.0f, 1.0f));
        return;
    }

    t_near = (t_near < 0) ? 0 : t_near;
    float t = t_near;
    float4 color = (float4)(0.0f, 0.0f, 0.0f, 0.0f);

    int i;
    for (i = 0; i < max_iteration; ++i) {
      float3 p = camera + t * direction_world;

      // (-1.0f, 1.0f) -> (0.0f, 1.0f) -> (0, 512)
      int4 volume_index = (int4)((int)((p.x + 1) * range.x),
                                 (int)((p.y + 1) * range.y),
                                 (int)((p.z + 1) * range.z),
                                 1);

      color += read_imagef(volume, sampler, volume_index);
      t += step_size;
      if (t > t_far) break;
    }
    write_imagef(image, uv, 0.006 * (float4)(0.0f, color.y, 0.0f, 1.0f));
}