Fix keys used in produce fills

Fix keys used in produce fills

/******************************************************************************
 * function: wah_bitmap                                                       *
 * input:    32-bit integer                                                   *
 * output:   wah compressed bitmap index, offsets for its values              *
 ******************************************************************************/

#define FENCE_TYPE CLK_LOCAL_MEM_FENCE
#define WORK_GROUP_SIZE 1024

// Processing steps
void sort_rids_by_value(global uint* input, global  uint* rids,
                        size_t idx, size_t total);
void produce_chunck_id_literals(global uint* rids, global uint* chids,
                                global uint* lits, size_t idx, size_t total);
size_t merged_lit_by_val_chids(global uint* input, global uint* chids,
                               global uint* lits, size_t idx, size_t total);
void produce_fills(global uint* input, global uint* chids,
                   size_t idx, size_t total);
size_t fuse_fill_literals(global uint* chids, global uint* lits,
                          global uint* index, size_t idx, size_t total);
size_t compute_colum_length(global uint* input, global uint* chids,
                            global uint* offsets, size_t idx, size_t total);

// helper functions
void parallel_selection_sort(global uint* key, global uint* data,
                             size_t idx, size_t total);
size_t reduce_by_key_OR(global uint* keys_high, global uint* keys_low,
                        global uint* data, size_t idx, size_t total);
size_t reduce_by_key_SUM(global uint* keys, local uint* data,
                         size_t idx, size_t total);

// main kernel
kernel void kernel_wah_index(global uint* config,
                             global uint* input,
                             global uint* index,
                             global uint* offsets,
                             global uint* rids,
                             global uint* chids,
                             global uint* lits) {
  // just 1 dimension here: 0
  size_t total = get_global_size(0);
  size_t idx = get_global_id(0);
  if (total > WORK_GROUP_SIZE) {
    offsets[idx] = total;
    index[idx] = WORK_GROUP_SIZE;
    return;
  }
  // CONSIDERATIONS:
  // * some scaling state of the are functions required
  //  - sorting
  //  - scan
  //  - stream compaction
  //  - reduce by key
  // * when do we need barriers?
  //  - at the end of functions, before return?
  //  - can be just assign return values to global stuff
  //  - ...

  // read config data
  // uint input_size       = config[0];
  // uint index_size       = config[1]; should be 2* input size
  // uint processed_ values = config[2];

  // Currently 1 work item per value
  total = config[0];

  //__gloabl uint[index_size] rids;
  sort_rids_by_value(input, rids, idx, total);
  barrier(FENCE_TYPE);
  //index[idx] = rids[idx];
  //offsets[idx] = 0;
  //config[4] = total;
  produce_chunck_id_literals(rids, chids, lits, idx, total);
  barrier(FENCE_TYPE);
  //input[idx] = rids[idx];
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  //config[4] = total;
  size_t k = merged_lit_by_val_chids(input, chids, lits, idx, total);
  barrier(FENCE_TYPE);
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  //config[3] = (uint) k;
  //config[4] = (uint) k;
  //config[5] = (uint) k;
  produce_fills(input, chids, idx, k);
  barrier(FENCE_TYPE);
  //config[1] = k;
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  uint idxlen = fuse_fill_literals(chids, lits, index, idx, k);
  barrier(FENCE_TYPE);
  config[1] = idxlen;
  uint keycnt = compute_colum_length(input, chids, offsets, idx, k);
  barrier(FENCE_TYPE);
  config[0] = keycnt;
}

void sort_rids_by_value(global uint* input,global uint* rids,
                        size_t idx, size_t total) {
  rids[idx] = idx;
  barrier(FENCE_TYPE); // is this needed here?
  // sort by input value
  parallel_selection_sort(input, rids, idx, total);
}

void produce_chunck_id_literals(global uint* rids, global uint* chids,
                                global uint* lits, size_t idx, size_t total) {
  (void) total;
  lits[idx] = 1u << (rids[idx] % 31u);
  lits[idx] |= 1u << 31;
  chids[idx] = (uint) rids[idx] / 31;
}

size_t merged_lit_by_val_chids(global uint* input, global uint* chids,
                               global uint* lits, size_t idx, size_t total) {
  // avoid merge of chids and input into keys,
  // simply pass them both
  size_t k = reduce_by_key_OR(chids, input, lits, idx, total);
  return k;
}

void produce_fills(global uint* input, global uint* chids,
                   size_t idx, size_t total) {
  uint tmp = chids[idx];
  if (idx != 0 && input[idx] == input[idx - 1]) {
    tmp = chids[idx] - chids[idx - 1] - 1;
  }
  // This branch leads to loss of fills at the beginning of
  // a bitmap index.
  /* else {
    if (chids[idx] != 0) {
      chids[idx] = chids[idx] - 1;
    }
  }*/
  barrier(FENCE_TYPE);
  chids[idx] = tmp;
}

size_t fuse_fill_literals(global uint* chids, global uint* lits,
                          global uint* index, size_t idx, size_t total) {
  local uint markers [WORK_GROUP_SIZE * 2];
  local uint position[WORK_GROUP_SIZE * 2];
  volatile local int len;
  uint a =  2 * idx;
  uint b = (2 * idx) + 1;
  len = 0;
  index[a] = chids[idx];
  index[b] = lits[idx];
  barrier(FENCE_TYPE);
  // stream compaction
  markers[a] = index[a] != 0; // ? 1 : 0;
  markers[b] = index[b] != 0; // ? 1 : 0;
  barrier(FENCE_TYPE);
  // should be a parallel scan
  if (idx == 0) {
    position[0] = 0;
    for (uint i = 1; i < (2 * total); ++i) {
      position[i] = position[i - 1] + markers[i - 1];
    }
  }
  // end parallel scan
  uint tmp_a = index[a];
  uint tmp_b = index[b];
  barrier(FENCE_TYPE);
  if (idx < total) {
    if (markers[a] == 1) {
      index[position[a]] = tmp_a;
      atomic_add(&len, 1);
    }
    if (markers[b] == 1) {
      index[position[b]] = tmp_b;
      atomic_add(&len, 1);
    }
  }
  barrier(FENCE_TYPE); // <-- should there be a barrier here?
  return len;
}

size_t compute_colum_length(global uint* input, global uint* chids,
                            global uint* offsets, size_t idx, size_t k) {
  local uint tmp[WORK_GROUP_SIZE];
  tmp[idx] = (1 + (chids[idx] == 0 ? 0 : 1));
  uint keycnt = reduce_by_key_SUM(input, tmp, idx, k);
  // inclusive scan to create offsets, should be parallel
  barrier(FENCE_TYPE);
  if (idx == 0) {
    offsets[0] = 0;
    for (uint i = 1; i < keycnt; ++i) {
      offsets[i] = offsets[i - 1] + tmp[i - 1];
    }
  }
  return keycnt;
}

// Helper functions

// we have 64 bit keys consisting of (high << 32 | low), but want to
// avoid the extra copy, so ...
size_t reduce_by_key_OR(global uint* keys_high, global uint* keys_low,
                        global uint* data, size_t idx, size_t total) {
  local uint heads[WORK_GROUP_SIZE];
  volatile local int k;
  heads[idx] = (idx == 0) ||
               (keys_high[idx] != keys_high[idx - 1]) ||
               (keys_low [idx] != keys_low [idx - 1]);
  barrier(FENCE_TYPE);
  if (heads[idx] != 0) {
    uint val = data[idx];
    uint curr = idx + 1;
    while (heads[curr] == 0 && curr < total) {
      val |= data[curr]; // OR operation
      curr += 1;
    }
    atomic_add(&k, 1);
    data[idx] = val;
  }
  barrier(FENCE_TYPE);
  if (idx == 0) {
    uint pos = 0;
    for (uint i = 0; i < total; ++i) {
      if (heads[i] != 0) {
        keys_high[pos] = keys_high[i];
        keys_low[pos] = keys_low[i];
        data[pos] = data[i];
        pos += 1;
      }
    }
  }
  return (uint) k;
}

size_t reduce_by_key_SUM(global uint* keys, local uint* data,
                         size_t idx, size_t total) {
  local uint heads[WORK_GROUP_SIZE];
  heads[idx] = idx == 0 || (keys[idx] != keys[idx - 1]);
  volatile local int k;
  if (heads[idx] != 0 && idx < total) {
    uint val = data[idx];
    uint curr = idx + 1;
    while (heads[curr] == 0 && curr < total) {
      val += data[curr]; // SUM operation
      curr += 1;
    }
    atomic_add(&k, 1);
    data[idx] = val;
  }
  barrier(FENCE_TYPE);
  if (idx == 0) {
    uint pos = 0;
    for (uint i = 0; i < total; ++i) {
      if (heads[i] != 0) {
        keys[pos] = keys[i];
        data[pos] = data[i];
        pos += 1;
      }
    }
  }
  return (uint) k;
}

/**
 * Sort found on: http://www.bealto.com/gpu-sorting_intro.html
 * License:set syntax=:
 *  This code is released under the following license (BSD-style).
 *  --
 *
 *  Copyright (c) 2011, Eric Bainville
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of Eric Bainville nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY ERIC BAINVILLE ''AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL ERIC BAINVILLE BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **/

void parallel_selection_sort(global uint* key, global uint* data,
                             size_t idx, size_t total) {
  uint key_value = key[idx];
  uint data_value = data[idx];
  // Compute position of in[i] in output
  int pos = 0;
  for (size_t j = 0; j < total; ++j) {
    uint curr = key[j]; // broadcasted
    bool smaller = (curr < key_value) || (curr == key_value && j < idx);
    pos += (smaller) ? 1 : 0;
  }
  barrier(CLK_LOCAL_MEM_FENCE);
  key[pos] = key_value;
  data[pos] = data_value;
}

/******************************************************************************
 * function: wah_bitmap                                                       *
 * input:    32-bit integer                                                   *
 * output:   wah compressed bitmap index, offsets for its values              *
 ******************************************************************************/

#define FENCE_TYPE CLK_LOCAL_MEM_FENCE
#define WORK_GROUP_SIZE 1024

// Processing steps
void sort_rids_by_value(global uint* input, global  uint* rids,
                        size_t idx, size_t total);
void produce_chunck_id_literals(global uint* rids, global uint* chids,
                                global uint* lits, size_t idx, size_t total);
size_t merged_lit_by_val_chids(global uint* input, global uint* chids,
                               global uint* lits, size_t idx, size_t total);
void produce_fills(global uint* input, global uint* chids,
                   size_t idx, size_t total);
size_t fuse_fill_literals(global uint* chids, global uint* lits,
                          global uint* index, size_t idx, size_t total);
size_t compute_colum_length(global uint* input, global uint* chids,
                            global uint* offsets, size_t idx, size_t total);

// helper functions
void parallel_selection_sort(global uint* key, global uint* data,
                             size_t idx, size_t total);
size_t reduce_by_key_OR(global uint* keys_high, global uint* keys_low,
                        global uint* data, size_t idx, size_t total);
size_t reduce_by_key_SUM(global uint* keys, local uint* data,
                         size_t idx, size_t total);

// main kernel
kernel void kernel_wah_index(global uint* config,
                             global uint* input,
                             global uint* index,
                             global uint* offsets,
                             global uint* rids,
                             global uint* chids,
                             global uint* lits) {
  // just 1 dimension here: 0
  size_t total = get_global_size(0);
  size_t idx = get_global_id(0);
  if (total > WORK_GROUP_SIZE) {
    offsets[idx] = total;
    index[idx] = WORK_GROUP_SIZE;
    return;
  }
  // CONSIDERATIONS:
  // * some scaling state of the are functions required
  //  - sorting
  //  - scan
  //  - stream compaction
  //  - reduce by key
  // * when do we need barriers?
  //  - at the end of functions, before return?
  //  - can be just assign return values to global stuff
  //  - ...

  // read config data
  // uint input_size       = config[0];
  // uint index_size       = config[1]; should be 2* input size
  // uint processed_ values = config[2];

  // Currently 1 work item per value
  total = config[0];

  //__gloabl uint[index_size] rids;
  sort_rids_by_value(input, rids, idx, total);
  barrier(FENCE_TYPE);
  //index[idx] = rids[idx];
  //offsets[idx] = 0;
  //config[4] = total;
  produce_chunck_id_literals(rids, chids, lits, idx, total);
  barrier(FENCE_TYPE);
  //input[idx] = rids[idx];
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  //config[4] = total;
  size_t k = merged_lit_by_val_chids(input, chids, lits, idx, total);
  barrier(FENCE_TYPE);
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  //config[3] = (uint) k;
  //config[4] = (uint) k;
  //config[5] = (uint) k;
  produce_fills(input, chids, idx, k);
  barrier(FENCE_TYPE);
  //config[4] = k;
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  uint idxlen = fuse_fill_literals(chids, lits, index, idx, k);
  barrier(FENCE_TYPE);
  config[1] = idxlen;
  uint keycnt = compute_colum_length(input, chids, offsets, idx, k);
  barrier(FENCE_TYPE);
  config[0] = keycnt;
}

void sort_rids_by_value(global uint* input,global uint* rids,
                        size_t idx, size_t total) {
  rids[idx] = idx;
  barrier(FENCE_TYPE); // is this needed here?
  // sort by input value
  parallel_selection_sort(input, rids, idx, total);
}

void produce_chunck_id_literals(global uint* rids, global uint* chids,
                                global uint* lits, size_t idx, size_t total) {
  (void) total;
  lits[idx] = 1u << (rids[idx] % 31u);
  lits[idx] |= 1u << 31;
  chids[idx] = (uint) rids[idx] / 31;
}

size_t merged_lit_by_val_chids(global uint* input, global uint* chids,
                               global uint* lits, size_t idx, size_t total) {
  // avoid merge of chids and input into keys,
  // simply pass them both
  size_t k = reduce_by_key_OR(chids, input, lits, idx, total);
  return k;
}

void produce_fills(global uint* input, global uint* chids,
                   size_t idx, size_t total) {
  uint tmp = chids[idx];
  if (idx != 0 && (input[idx] == input[idx - 1] &&
                   chids[idx] == chids[idx - 1])) {
    tmp = chids[idx] - chids[idx - 1] - 1;
  }
  // This branch leads to loss of fills at the beginning of
  // a bitmap index.
  /* else {
    if (chids[idx] != 0) {
      chids[idx] = chids[idx] - 1;
    }
  }*/
  barrier(FENCE_TYPE);
  chids[idx] = tmp;
}

size_t fuse_fill_literals(global uint* chids, global uint* lits,
                          global uint* index, size_t idx, size_t total) {
  local uint markers [WORK_GROUP_SIZE * 2];
  local uint position[WORK_GROUP_SIZE * 2];
  volatile local int len;
  //uint a =  2 * idx;
  //uint b = (2 * idx) + 1;
  len = 0;
  index[ 2 * idx     ] = chids[idx];
  index[(2 * idx) + 1] = lits[idx];
  barrier(FENCE_TYPE);
  // stream compaction
  markers[ 2 * idx     ] = index[ 2 * idx     ] != 0; // ? 1 : 0;
  markers[(2 * idx) + 1] = index[(2 * idx) + 1] != 0; // ? 1 : 0;
  barrier(FENCE_TYPE);
  // should be a parallel scan
  if (idx == 0) {
    position[0] = 0;
    for (uint i = 1; i < 2 * total; ++i) {
      position[i] = position[i - 1] + markers[i - 1];
    }
  }
  // end parallel scan
  uint tmp_a = index[2 * idx];
  uint tmp_b = index[(2 * idx) + 1];
  barrier(FENCE_TYPE);
  if (idx < total) {
    if (markers[2 * idx] == 1) {
      index[position[2 * idx]] = tmp_a;
      atomic_add(&len, 1);
    }
    if (markers[(2 * idx) + 1] == 1) {
      index[position[(2 * idx) + 1]] = tmp_b;
      atomic_add(&len, 1);
    }
  }
  barrier(FENCE_TYPE); // <-- should there be a barrier here?
  return len;
}

size_t compute_colum_length(global uint* input, global uint* chids,
                            global uint* offsets, size_t idx, size_t k) {
  local uint tmp[WORK_GROUP_SIZE];
  tmp[idx] = (1 + (chids[idx] == 0 ? 0 : 1));
  uint keycnt = reduce_by_key_SUM(input, tmp, idx, k);
  // inclusive scan to create offsets, should be parallel
  barrier(FENCE_TYPE);
  if (idx == 0) {
    offsets[0] = 0;
    for (uint i = 1; i < keycnt; ++i) {
      offsets[i] = offsets[i - 1] + tmp[i - 1];
    }
  }
  return keycnt;
}

// Helper functions

// we have 64 bit keys consisting of (high << 32 | low), but want to
// avoid the extra copy, so ...
size_t reduce_by_key_OR(global uint* keys_high, global uint* keys_low,
                        global uint* data, size_t idx, size_t total) {
  local uint heads[WORK_GROUP_SIZE];
  volatile local int k;
  heads[idx] = (idx == 0) ||
               (keys_high[idx] != keys_high[idx - 1]) ||
               (keys_low [idx] != keys_low [idx - 1]);
  barrier(FENCE_TYPE);
  if (heads[idx] != 0) {
    uint val = data[idx];
    uint curr = idx + 1;
    while (heads[curr] == 0 && curr < total) {
      val |= data[curr]; // OR operation
      curr += 1;
    }
    atomic_add(&k, 1);
    data[idx] = val;
  }
  barrier(FENCE_TYPE);
  if (idx == 0) {
    uint pos = 0;
    for (uint i = 0; i < total; ++i) {
      if (heads[i] != 0) {
        keys_high[pos] = keys_high[i];
        keys_low[pos] = keys_low[i];
        data[pos] = data[i];
        pos += 1;
      }
    }
  }
  return (uint) k;
}

size_t reduce_by_key_SUM(global uint* keys, local uint* data,
                         size_t idx, size_t total) {
  local uint heads[WORK_GROUP_SIZE];
  heads[idx] = idx == 0 || (keys[idx] != keys[idx - 1]);
  volatile local int k;
  if (heads[idx] != 0 && idx < total) {
    uint val = data[idx];
    uint curr = idx + 1;
    while (heads[curr] == 0 && curr < total) {
      val += data[curr]; // SUM operation
      curr += 1;
    }
    atomic_add(&k, 1);
    data[idx] = val;
  }
  barrier(FENCE_TYPE);
  if (idx == 0) {
    uint pos = 0;
    for (uint i = 0; i < total; ++i) {
      if (heads[i] != 0) {
        keys[pos] = keys[i];
        data[pos] = data[i];
        pos += 1;
      }
    }
  }
  return (uint) k;
}

/**
 * Sort found on: http://www.bealto.com/gpu-sorting_intro.html
 * License:set syntax=:
 *  This code is released under the following license (BSD-style).
 *  --
 *
 *  Copyright (c) 2011, Eric Bainville
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of Eric Bainville nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY ERIC BAINVILLE ''AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL ERIC BAINVILLE BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **/

void parallel_selection_sort(global uint* key, global uint* data,
                             size_t idx, size_t total) {
  uint key_value = key[idx];
  uint data_value = data[idx];
  // Compute position of in[i] in output
  int pos = 0;
  for (size_t j = 0; j < total; ++j) {
    uint curr = key[j]; // broadcasted
    bool smaller = (curr < key_value) || (curr == key_value && j < idx);
    pos += (smaller) ? 1 : 0;
  }
  barrier(CLK_LOCAL_MEM_FENCE);
  key[pos] = key_value;
  data[pos] = data_value;
}


while loop to load into LDS

while loop to load into LDS

#if 0
kernel void Im2Col(global float *im, size_t im_offset,
		const int h, const int w,
		const int wei_h, const int wei_w,
		const int out_h, const int out_w,
		const int pad_h, const int pad_w,
		const int stride_h, const int stride_w,
		global float *col)
{
	int tid = get_global_id(0);
	int col_row = tid / (out_h * out_w);

	int im_x = col_row % wei_w;
	int im_y = (col_row / wei_w) % wei_h;
	int im_c = col_row / (wei_w * wei_h);

	int out_x = tid % out_w;
	int out_y = (tid / out_w) % out_h;

	int im_off_h = out_y * stride_h - pad_h + im_y;
	int im_off_w = out_x * stride_w - pad_w + im_x;

	global float *im_off = (global float *)&im[im_offset];

	if(im_off_h >= 0 && im_off_h < h && im_off_w >= 0 && im_off_w < w) {
		col[col_row*out_h*out_w + out_y*out_w + out_x] = im_off[im_c*h*w + im_off_h*w + im_off_w];
	}
	else {
		col[col_row*out_h*out_w + out_y*out_w + out_x] = 0.;
	}
}
#endif
kernel void Im2Col(global float *im, size_t im_offset,
		const int h, const int w,
		const int wei_h, const int wei_w,
		const int out_h, const int out_w,
		const int pad_h, const int pad_w,
		const int stride_h, const int stride_w,
		global float *col)
{
#define THREADS_PER_CH (256 / NUM_CH_PER_WG)

	global float *im_off = im + im_offset;
	int lid = get_local_id(0);
	int gid = get_group_id(0);

#if NUM_IM_BLKS == 1

	// Load image into LDS
	local float local_im[256];

	int witem_ch = lid / THREADS_PER_CH;
	if(lid < NUM_CH_PER_WG*h*w)
		local_im[lid] = im_off[(gid*NUM_CH_PER_WG)*h*w + lid];
	barrier(CLK_LOCAL_MEM_FENCE);

	// where will each thread to col
	int witem_ch_offset = witem_ch*h*w;

	if(lid % THREADS_PER_CH < out_h*out_w) {
		int inner_lid = lid % THREADS_PER_CH;
		int out_x = inner_lid % out_w;
		int out_y = inner_lid / out_w;

		int col_x = out_y * out_w + out_x;
		int col_y = (gid*NUM_CH_PER_WG+witem_ch) * out_h * out_w * wei_h * wei_w;

		for(int y = 0; y < wei_h; y++) {
			for(int x = 0; x < wei_w; x++) {
				int im_off_h = out_y * stride_h - pad_h + y;
				int im_off_w = out_x * stride_w - pad_w + x;
				if(im_off_h >= 0 && im_off_h < h && im_off_w >= 0 && im_off_w < w)
					col[col_y + col_x + (y*wei_w+x)*out_h*out_w] = local_im[witem_ch_offset + (im_off_h)*h + im_off_w];
				else
					col[col_y + col_x + (y*wei_w+x)*out_h*out_w] = 0;
			}
		}
	}

#else // NUM_IM_BLKS > 1

	local float local_im[LOCAL_MEM_SIZE];

	int witem_ch = lid / THREADS_PER_CH;
	int wg_ch = gid / NUM_IM_BLKS;

	int im_x = ((gid % NUM_IM_BLKS) % NUM_IM_BLKS_X) * 16;
	int im_y = ((gid % NUM_IM_BLKS) / NUM_IM_BLKS_X) * 16;

	int out_cols_wg = im_x + 16 <= out_w ? 16 : out_w - im_x;
	int out_rows_wg = im_y + 16 <= out_h ? 16 : out_h - im_y;

	int im_cols_wg = 16+ 2*pad_w;
	int inner_lid = lid;

	while (inner_lid < LOCAL_MEM_SIZE) {
		int row_to_use = inner_lid / im_cols_wg;
		int col_to_use = inner_lid % im_cols_wg;
		int lm_offset = row_to_use*im_cols_wg + col_to_use;
		if(im_y + row_to_use >= pad_h && im_y + row_to_use < h+pad_h && im_x + col_to_use >= pad_w && im_x + col_to_use < w+pad_w) {
			int im_off_h = im_y + row_to_use - pad_h;
			int im_off_w = im_x + col_to_use - pad_w;
			local_im[lm_offset] = im_off[wg_ch*h*w + im_off_h*w + im_off_w];
		}
		else
			local_im[lm_offset] = 0;

		inner_lid += 256;
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	if(lid < out_cols_wg * out_rows_wg) {
		int out_x = lid % out_cols_wg;
		int out_y = lid / out_cols_wg;

		int col_x = (im_y + out_y) * out_w + im_x + out_x;
		int col_y = (gid / NUM_IM_BLKS) * out_h * out_w * wei_h * wei_w;

		for(int y = 0; y < wei_h; y++) {
			for(int x = 0; x < wei_w; x++) {
				int im_off_h = out_y + y;
				int im_off_w = out_x + x;
				col[col_y + col_x + (y*wei_w+x)*out_h*out_w] = local_im[(im_off_h)*im_cols_wg + im_off_w];
			}
		}

	}
#endif
}

#if 0
kernel void Im2Col(global float *im, size_t im_offset,
		const int h, const int w,
		const int wei_h, const int wei_w,
		const int out_h, const int out_w,
		const int pad_h, const int pad_w,
		const int stride_h, const int stride_w,
		global float *col)
{
	int tid = get_global_id(0);
	int col_row = tid / (out_h * out_w);

	int im_x = col_row % wei_w;
	int im_y = (col_row / wei_w) % wei_h;
	int im_c = col_row / (wei_w * wei_h);

	int out_x = tid % out_w;
	int out_y = (tid / out_w) % out_h;

	int im_off_h = out_y * stride_h - pad_h + im_y;
	int im_off_w = out_x * stride_w - pad_w + im_x;

	global float *im_off = (global float *)&im[im_offset];

	if(im_off_h >= 0 && im_off_h < h && im_off_w >= 0 && im_off_w < w) {
		col[col_row*out_h*out_w + out_y*out_w + out_x] = im_off[im_c*h*w + im_off_h*w + im_off_w];
	}
	else {
		col[col_row*out_h*out_w + out_y*out_w + out_x] = 0.;
	}
}
#endif
kernel void Im2Col(global float *im, size_t im_offset,
		const int h, const int w,
		const int wei_h, const int wei_w,
		const int out_h, const int out_w,
		const int pad_h, const int pad_w,
		const int stride_h, const int stride_w,
		global float *col)
{
#define THREADS_PER_CH (256 / NUM_CH_PER_WG)

	global float *im_off = im + im_offset;
	int lid = get_local_id(0);
	int gid = get_group_id(0);

#if NUM_IM_BLKS == 1

	// Load image into LDS
	local float local_im[256];

	int witem_ch = lid / THREADS_PER_CH;
	if(lid < NUM_CH_PER_WG*h*w)
		local_im[lid] = im_off[(gid*NUM_CH_PER_WG)*h*w + lid];
	barrier(CLK_LOCAL_MEM_FENCE);

	// where will each thread to col
	int witem_ch_offset = witem_ch*h*w;

	if(lid % THREADS_PER_CH < out_h*out_w) {
		int inner_lid = lid % THREADS_PER_CH;
		int out_x = inner_lid % out_w;
		int out_y = inner_lid / out_w;

		int col_x = out_y * out_w + out_x;
		int col_y = (gid*NUM_CH_PER_WG+witem_ch) * out_h * out_w * wei_h * wei_w;

		for(int y = 0; y < wei_h; y++) {
			for(int x = 0; x < wei_w; x++) {
				int im_off_h = out_y * stride_h - pad_h + y;
				int im_off_w = out_x * stride_w - pad_w + x;
				if(im_off_h >= 0 && im_off_h < h && im_off_w >= 0 && im_off_w < w)
					col[col_y + col_x + (y*wei_w+x)*out_h*out_w] = local_im[witem_ch_offset + (im_off_h)*h + im_off_w];
				else
					col[col_y + col_x + (y*wei_w+x)*out_h*out_w] = 0;
			}
		}
	}

#else // NUM_IM_BLKS > 1

	local float local_im[LOCAL_MEM_SIZE];

	int witem_ch = lid / THREADS_PER_CH;
	int wg_ch = gid / NUM_IM_BLKS;

	int im_x = ((gid % NUM_IM_BLKS) % NUM_IM_BLKS_X) * 16;
	int im_y = ((gid % NUM_IM_BLKS) / NUM_IM_BLKS_X) * 16;

	int out_cols_wg = im_x + 16 <= out_w ? 16 : out_w - im_x;
	int out_rows_wg = im_y + 16 <= out_h ? 16 : out_h - im_y;

	int im_cols_wg = 16 + 2*pad_w;
	int inner_lid = lid;
	int row_to_use = inner_lid / im_cols_wg;
	int col_to_use = inner_lid % im_cols_wg;
	int lm_offset = row_to_use*im_cols_wg + col_to_use;
	if(im_y + row_to_use >= pad_h && im_y + row_to_use < h+pad_h && im_x + col_to_use >= pad_w && im_x + col_to_use < w+pad_w) {
		int im_off_h = im_y + row_to_use - pad_h;
		int im_off_w = im_x + col_to_use - pad_w;
		local_im[lm_offset] = im_off[wg_ch*h*w + im_off_h*w + im_off_w];
	}
	else
		local_im[lm_offset] = 0;

	inner_lid += 256;
	if(inner_lid < LOCAL_MEM_SIZE) {
		int row_to_use = inner_lid / im_cols_wg;
		int col_to_use = inner_lid % im_cols_wg;
		int lm_offset = row_to_use*im_cols_wg + col_to_use;
		if(im_y + row_to_use >= pad_h && im_y + row_to_use < h+pad_h && im_x + col_to_use >= pad_w && im_x + col_to_use < w+pad_w) {
			int im_off_h = im_y + row_to_use - pad_h;
			int im_off_w = im_x + col_to_use - pad_w;
			local_im[lm_offset] = im_off[wg_ch*h*w + im_off_h*w + im_off_w];
		}
		else
			local_im[lm_offset] = 0;
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	if(lid < out_cols_wg * out_rows_wg) {
		int out_x = lid % out_cols_wg;
		int out_y = lid / out_cols_wg;

		int col_x = (im_y + out_y) * out_w + im_x + out_x;
		int col_y = (gid / NUM_IM_BLKS) * out_h * out_w * wei_h * wei_w;

		for(int y = 0; y < wei_h; y++) {
			for(int x = 0; x < wei_w; x++) {
				int im_off_h = out_y + y;
				int im_off_w = out_x + x;
				col[col_y + col_x + (y*wei_w+x)*out_h*out_w] = local_im[(im_off_h)*im_cols_wg + im_off_w];
			}
		}

	}
#endif
}


Fix graphical glitches on portals that exit straight onto a wall.

/* Size of the texture currently hard-coded */
#define SCREEN_W 640
#define SCREEN_H 480

/* With the current algorithm, this is the maximum number of voxels to
   traverse */
#define LOOP_LIMIT 100

/* Colors for the various sides of wall blocks */
#define COLOR_WALL  ((float4)(0.45,0.75,0.75,1.0))
#define COLOR_WALLG  ((float4)(0.45,0.75,0.45,1.0))

/* Multipliers for calculating shading on block faces */
#define LIGHTEN (4.0/3.0)
#define DARKEN (2.0/3.0)

/* Edge types - what appears at the end of the grid */
#define ET_WALL         0
#define ET_PORTAL_TORUS 1
#define ET_SOLID_AIR    2

/* Block types */
#define BK_AIR      0
#define BK_WALL     1
#define BK_WALLG    2
#define BK_PORTAL(X)  (255-(X))

/* Handy macro for computing nearest axis boundary */
#define POSITIVE(X) ((X) > 0 ? (X) : 0)

/* Offset of grid within mapdat */
#define GRID_OFF 4

typedef struct world_t {
    uchar4 bounds;
    uint edge_type;
    __constant uchar *grid;
    __constant float *portals;
} world_t;

typedef struct ray_t {
    int4 P;
    float4 ray_color;
    uint outside;
    char4 last_step;
} ray_t;

/* `shading' values > 1.0 brighten, < 1.0 darken. */
float4 color_mix_shade(float4 color, float shading) {
    return (float4)(fmin(color.x * shading, 1.0f),
                    fmin(color.y * shading, 1.0f),
                    fmin(color.z * shading, 1.0f),
                    color.w);
}

float4 vec_add_f4_c4(float4 a, char4 b) {
    return (float4)(a.x + b.x,
                    a.y + b.y,
                    a.z + b.z,
                    a.w + b.w);
}

int4 vec_add_i4_c4(int4 a, char4 b) {
    return (int4)(a.x + b.x,
                  a.y + b.y,
                  a.z + b.z,
                  a.w + b.w);
}

char4 vec_scale_c_c4(char s, char4 v) {
    return (char4)(s*v.x,
                   s*v.y,
                   s*v.z,
                   s*v.w);
}

float dot_prod(float4 i, float4 j) {
    return i.x * j.x + i.y * j.y + i.z * j.z + i.w * j.w;
}

float4 apply_matrix(float16 m_, float4 v) {
    union {
        float16 f;
        float m[16];
    } mu;
    mu.f = m_;
    float *m = mu.m;
    #define ELE(i) (m[i]*v.x + m[i+1]*v.y + m[i+2]*v.z + m[i+3]*v.w)
    return (float4)(ELE(0), ELE(4), ELE(8), ELE(12));
    #undef ELE
}

uchar grid_get(world_t w, int4 P) {
    return w.grid[P.x + (P.y * w.bounds.x) +
        (P.z * w.bounds.x * w.bounds.y)];
}

/* Returns the portal number if the block is a portal, or otherwise -1. */
char is_portal_block(uchar block_type) {
    return block_type >= BK_PORTAL(8) && block_type <= BK_PORTAL(0)
        ? 255 - block_type
        : -1;
}

float16 get_portal(world_t w, uchar i) {
    __constant float *p = &w.portals[i*16];
    float16 result = (float16)(
        p[0], p[1], p[2], p[3],
        p[4], p[5], p[6], p[7],
        p[8], p[9], p[10], p[11],
        p[12], p[13], p[14], p[15]);
    return result;
}

/* Returns new ray_color. Halts ray if alpha is 1.0f.
   v is the unit vector of the ray; t is how far down the ray we are. */
float4 color_ray(world_t w, ray_t r, float4 v, float t)
{
    uchar block_type = BK_AIR;

    if (r.outside) {
        if (w.edge_type == ET_WALL) {
            block_type = BK_WALL;
        } else {
            /* else leave block_type as BK_AIR */
            // TODO ET_PORTAL_TORUS
            return (float4)(0.0f, 0.0f, 0.0f, 1.0f);
        }
    } else {
        block_type = grid_get(w, r.P);
    }

    float4 new_ray_color = r.ray_color;

    if (block_type != BK_AIR) {
        bool set_color = false;

        switch (block_type) {
        case BK_WALL:
            new_ray_color = COLOR_WALL;
            set_color = true;
            break;
        case BK_WALLG:
            new_ray_color = COLOR_WALLG;
            set_color = true;
            break;
        }

        if (set_color) {
            /* Calculate ambient lighting on this face */
            if (r.last_step.y == -1) {
                // viewing block from above
                new_ray_color = color_mix_shade(new_ray_color, LIGHTEN);
            } else if (r.last_step.y == 1) {
                // viewing block from below
                new_ray_color = color_mix_shade(new_ray_color, DARKEN);
            }

            /* Calculate diffuse lighting, reusing camera's position
               as light source. */
            float4 normal = (float4)(-r.last_step.x,
                                   -r.last_step.y,
                                   -r.last_step.z,
                                   -r.last_step.w);
            float diffuse_light = dot_prod(normal, v);
            /* The dot-product gives us -1.0 for fully lit surfaces,
               and 0.0 or < 0.0 for orthogonal and far surfaces */
            if (diffuse_light < 0.0f)
                /* Some magic numbers to make things look nice.
                   Technically, it should be 1/t^2, but who cares about
                   realism? */
                diffuse_light = 0.5f - diffuse_light / (0.6f * t);
            else
                diffuse_light = 0.0f;
            new_ray_color = color_mix_shade(new_ray_color, diffuse_light);
        }
    }

    return new_ray_color;
}

__kernel void raytrace(__write_only __global image2d_t bmp,
    float rot_x, float rot_y, float cam_x, float cam_y, float cam_z,
    float fov_x, float fov_y, __constant uchar *mapdat)
{
    world_t w;
    w.bounds = (uchar4)(mapdat[0], mapdat[1], mapdat[2], 0); /* Grid dimensions */
    w.edge_type = mapdat[3];
    w.grid = &mapdat[GRID_OFF];
    w.portals = (__constant float *)&mapdat[GRID_OFF +
        w.bounds.x*w.bounds.y*w.bounds.z];

    /* Screen pixel position: */
    int2 p_pos = (int2)(get_global_id(0), get_global_id(1));

    float4 ray_rot = (float4)(rot_x, rot_y,
                              (p_pos.x * fov_x) / SCREEN_W - fov_x/2,
                              (p_pos.y * fov_y) / SCREEN_H - fov_y/2);
    float4 rot_sin = sin(ray_rot),
           rot_cos = cos(ray_rot);

    /* Voxel traversal ray-tracing algorithm based on John Amanatides'
       and Andrew Woo's paper. */

    /* n is the vector of the ray as if the camera was looking directly
       down the z-axis without any rotation around it.
       Multiply it by rotation matrices to get v (below) */
    float4 n = (float4)(rot_cos.w * rot_sin.z,
                        -rot_sin.w,
                        rot_cos.w * rot_cos.z,
                        0.0f);

    ray_t r;

    // v is the unit vector along the ray in actual world coords
    float4 v = (float4)(
      n.x*rot_cos.x + n.y*rot_sin.x*rot_sin.y + n.z*rot_sin.x*rot_cos.y,
      n.y*rot_cos.y - n.z*rot_sin.y,
      -n.x*rot_sin.x + n.y*rot_cos.x*rot_sin.y + n.z*rot_cos.x*rot_cos.y,
      0.0f);

    /* u is the starting point of the ray */
    float4 u = (float4)(cam_x, cam_y, cam_z, 1.0f);
    // Thus equation of the ray = u + vt

    float t = 0.0f;

    // P = (X,Y,Z) = current voxel coordinates
    r.P = (int4)((int)u.x, (int)u.y, (int)u.z, 1);
    // TODO handle P being outside bounds of grid

    /* step = (stepX, stepY, stepZ) = values are either 1, 0, or -1.
       Components are determined from the sign of the components of v */
    char4 step;
    #define RESET_STEP do { \
        step = (char4)((char)sign(v.x), (char)sign(v.y), \
                       (char)sign(v.z), 0); \
    } while (0)
    RESET_STEP;

    /* tmax = (tmaxX, tmaxY, tmaxZ) = values of t at which ray next
       crosses a voxel boundary in the respective direction.
       In RESET_TMAX, the "step.dim ?" conditionalisation is because we want
       infinity, not NaN, when v.dim is 0. NaN would cause an infinite
       loop in portal rendering. */
    float4 tmax;
    #define RESET_TMAX do { \
        tmax = (float4)( \
            step.x ? (r.P.x + POSITIVE(step.x) - u.x)/v.x : 1.0/0.0, \
            step.y ? (r.P.y + POSITIVE(step.y) - u.y)/v.y : 1.0/0.0, \
            step.z ? (r.P.z + POSITIVE(step.z) - u.z)/v.z : 1.0/0.0, \
            0.0f); \
    } while (0)
    RESET_TMAX;

    /* dt = (dtX, dtY, dtZ) = how far along ray (in units of t) we must
       move for x/y/z component of move to equal width of one voxel */
    float4 dt;
    #define RESET_DT do { \
        dt = (float4)(step.x / v.x, step.y / v.y, step.z / v.z, 0.0f); \
    } while (0);
    RESET_DT;

    /* justOut vector - used for checking we remain within the bounds
       of the grid */
    int4 justOut;
    #define RESET_JUSTOUT do { \
        justOut = (int4)(step.x == -1 ? -1 : w.bounds.x, \
                         step.y == -1 ? -1 : w.bounds.y, \
                         step.z == -1 ? -1 : w.bounds.z, \
                         0); \
    } while (0)
    RESET_JUSTOUT;

    r.ray_color = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
    r.outside = false;
    r.last_step = (char4)(0,0,0,0);

    // FIXME assuming camera is always inside the grid
    r.ray_color = color_ray(w, r, v, t);

    #define STEP(dim) do { \
                        r.P.dim += step.dim; \
                        r.last_step = (char4)(0,0,0,0); \
                        r.last_step.dim = step.dim; \
                        t = tmax.dim; \
                        if (r.P.dim == justOut.dim) { \
                            r.outside = true; \
                        } else \
                        tmax.dim += dt.dim; \
                      } while (0)

    for (uint iter_count = 0; iter_count < LOOP_LIMIT;
         iter_count++)
    {

        if (tmax.x < tmax.y) {
            if (tmax.x < tmax.z) {
                STEP(x);
            } else {
                STEP(z);
            }
        } else {
            if (tmax.y < tmax.z) {
                STEP(y);
            } else {
                STEP(z);
            }
        }

        char portal_num = -1;
        if (!r.outside &&
            (portal_num = is_portal_block(grid_get(w, r.P))) != -1)
        {
            float16 portal = get_portal(w, portal_num);

            // Step to the opposite (back) face of the portal
            char steps = 1;
            r.P = vec_add_i4_c4(r.P, r.last_step);
            while (r.P.x != justOut.x &&
                r.P.y != justOut.y &&
                r.P.z != justOut.z &&
                is_portal_block(grid_get(w, r.P)) != -1)
            {
                r.P = vec_add_i4_c4(r.P, r.last_step);
                steps ++;
            }
            u = vec_add_f4_c4(u, vec_scale_c_c4(steps, r.last_step));

            // Now apply the portal transformation
            u = apply_matrix(portal, u);
            v = apply_matrix(portal, v);
            r.P = (int4)((int)(u.x + v.x*t),
                         (int)(u.y + v.y*t),
                         (int)(u.z + v.z*t),
                         1);
            RESET_STEP;
            RESET_DT;
            RESET_TMAX;

            RESET_JUSTOUT;

            /*if (r.P.x < 0 || r.P.x >= w.bounds.x ||
                r.P.y < 0 || r.P.y >= w.bounds.y ||
                r.P.z < 0 || r.P.z >= w.bounds.z)
            {
                r.ray_color = (float4)(0.0,1.0,0.0,1.0);
            }*/

        }

        r.ray_color = color_ray(w, r, v, t);

        if (r.ray_color.w == 1.0f)
            break;

    }

    write_imagef(bmp, p_pos, r.ray_color);
}

/* Size of the texture currently hard-coded */
#define SCREEN_W 640
#define SCREEN_H 480

/* With the current algorithm, this is the maximum number of voxels to
   traverse */
#define LOOP_LIMIT 100

/* Colors for the various sides of wall blocks */
#define COLOR_WALL  ((float4)(0.45,0.75,0.75,1.0))
#define COLOR_WALLG  ((float4)(0.45,0.75,0.45,1.0))

/* Multipliers for calculating shading on block faces */
#define LIGHTEN (4.0/3.0)
#define DARKEN (2.0/3.0)

/* Edge types - what appears at the end of the grid */
#define ET_WALL         0
#define ET_PORTAL_TORUS 1
#define ET_SOLID_AIR    2

/* Block types */
#define BK_AIR      0
#define BK_WALL     1
#define BK_WALLG    2
#define BK_PORTAL(X)  (255-(X))

/* Handy macro for computing nearest axis boundary */
#define POSITIVE(X) ((X) > 0 ? (X) : 0)

/* Offset of grid within mapdat */
#define GRID_OFF 4

typedef struct world_t {
    uchar4 bounds;
    uint edge_type;
    __constant uchar *grid;
    __constant float *portals;
} world_t;

typedef struct ray_t {
    int4 P;
    float4 ray_color;
    uint outside;
    char4 last_step;
} ray_t;

/* `shading' values > 1.0 brighten, < 1.0 darken. */
float4 color_mix_shade(float4 color, float shading) {
    return (float4)(fmin(color.x * shading, 1.0f),
                    fmin(color.y * shading, 1.0f),
                    fmin(color.z * shading, 1.0f),
                    color.w);
}

float4 vec_add_f4_c4(float4 a, char4 b) {
    return (float4)(a.x + b.x,
                    a.y + b.y,
                    a.z + b.z,
                    a.w + b.w);
}

int4 vec_add_i4_c4(int4 a, char4 b) {
    return (int4)(a.x + b.x,
                  a.y + b.y,
                  a.z + b.z,
                  a.w + b.w);
}

char4 vec_scale_c_c4(char s, char4 v) {
    return (char4)(s*v.x,
                   s*v.y,
                   s*v.z,
                   s*v.w);
}

float dot_prod(float4 i, float4 j) {
    return i.x * j.x + i.y * j.y + i.z * j.z + i.w * j.w;
}

float4 apply_matrix(float16 m_, float4 v) {
    union {
        float16 f;
        float m[16];
    } mu;
    mu.f = m_;
    float *m = mu.m;
    #define ELE(i) (m[i]*v.x + m[i+1]*v.y + m[i+2]*v.z + m[i+3]*v.w)
    return (float4)(ELE(0), ELE(4), ELE(8), ELE(12));
    #undef ELE
}

uchar grid_get(world_t w, int4 P) {
    return w.grid[P.x + (P.y * w.bounds.x) +
        (P.z * w.bounds.x * w.bounds.y)];
}

/* Returns the portal number if the block is a portal, or otherwise -1. */
char is_portal_block(uchar block_type) {
    return block_type >= BK_PORTAL(8) && block_type <= BK_PORTAL(0)
        ? 255 - block_type
        : -1;
}

float16 get_portal(world_t w, uchar i) {
    __constant float *p = &w.portals[i*16];
    float16 result = (float16)(
        p[0], p[1], p[2], p[3],
        p[4], p[5], p[6], p[7],
        p[8], p[9], p[10], p[11],
        p[12], p[13], p[14], p[15]);
    return result;
}

/* Returns new ray_color. Halts ray if alpha is 1.0f.
   v is the unit vector of the ray; t is how far down the ray we are. */
float4 color_ray(world_t w, ray_t r, float4 v, float t)
{
    uchar block_type = BK_AIR;

    if (r.outside) {
        if (w.edge_type == ET_WALL) {
            block_type = BK_WALL;
        } else {
            /* else leave block_type as BK_AIR */
            // TODO ET_PORTAL_TORUS
            return (float4)(0.0f, 0.0f, 0.0f, 1.0f);
        }
    } else {
        block_type = grid_get(w, r.P);
    }

    float4 new_ray_color = r.ray_color;

    if (block_type != BK_AIR) {
        bool set_color = false;

        switch (block_type) {
        case BK_WALL:
            new_ray_color = COLOR_WALL;
            set_color = true;
            break;
        case BK_WALLG:
            new_ray_color = COLOR_WALLG;
            set_color = true;
            break;
        }

        if (set_color) {
            /* Calculate ambient lighting on this face */
            if (r.last_step.y == -1) {
                // viewing block from above
                new_ray_color = color_mix_shade(new_ray_color, LIGHTEN);
            } else if (r.last_step.y == 1) {
                // viewing block from below
                new_ray_color = color_mix_shade(new_ray_color, DARKEN);
            }

            /* Calculate diffuse lighting, reusing camera's position
               as light source. */
            float4 normal = (float4)(-r.last_step.x,
                                   -r.last_step.y,
                                   -r.last_step.z,
                                   -r.last_step.w);
            float diffuse_light = dot_prod(normal, v);
            /* The dot-product gives us -1.0 for fully lit surfaces,
               and 0.0 or < 0.0 for orthogonal and far surfaces */
            if (diffuse_light < 0.0f)
                /* Some magic numbers to make things look nice.
                   Technically, it should be 1/t^2, but who cares about
                   realism? */
                diffuse_light = 0.5f - diffuse_light / (0.6f * t);
            else
                diffuse_light = 0.0f;
            new_ray_color = color_mix_shade(new_ray_color, diffuse_light);
        }
    }

    return new_ray_color;
}

__kernel void raytrace(__write_only __global image2d_t bmp,
    float rot_x, float rot_y, float cam_x, float cam_y, float cam_z,
    float fov_x, float fov_y, __constant uchar *mapdat)
{
    world_t w;
    w.bounds = (uchar4)(mapdat[0], mapdat[1], mapdat[2], 0); /* Grid dimensions */
    w.edge_type = mapdat[3];
    w.grid = &mapdat[GRID_OFF];
    w.portals = (__constant float *)&mapdat[GRID_OFF +
        w.bounds.x*w.bounds.y*w.bounds.z];

    /* Screen pixel position: */
    int2 p_pos = (int2)(get_global_id(0), get_global_id(1));

    float4 ray_rot = (float4)(rot_x, rot_y,
                              (p_pos.x * fov_x) / SCREEN_W - fov_x/2,
                              (p_pos.y * fov_y) / SCREEN_H - fov_y/2);
    float4 rot_sin = sin(ray_rot),
           rot_cos = cos(ray_rot);

    /* Voxel traversal ray-tracing algorithm based on John Amanatides'
       and Andrew Woo's paper. */

    /* n is the vector of the ray as if the camera was looking directly
       down the z-axis without any rotation around it.
       Multiply it by rotation matrices to get v (below) */
    float4 n = (float4)(rot_cos.w * rot_sin.z,
                        -rot_sin.w,
                        rot_cos.w * rot_cos.z,
                        0.0f);

    ray_t r;

    // v is the unit vector along the ray in actual world coords
    float4 v = (float4)(
      n.x*rot_cos.x + n.y*rot_sin.x*rot_sin.y + n.z*rot_sin.x*rot_cos.y,
      n.y*rot_cos.y - n.z*rot_sin.y,
      -n.x*rot_sin.x + n.y*rot_cos.x*rot_sin.y + n.z*rot_cos.x*rot_cos.y,
      0.0f);

    /* u is the starting point of the ray */
    float4 u = (float4)(cam_x, cam_y, cam_z, 1.0f);
    // Thus equation of the ray = u + vt

    float t = 0.0f;

    // P = (X,Y,Z) = current voxel coordinates
    r.P = (int4)((int)u.x, (int)u.y, (int)u.z, 1);
    // TODO handle P being outside bounds of grid

    /* step = (stepX, stepY, stepZ) = values are either 1, 0, or -1.
       Components are determined from the sign of the components of v */
    char4 step;
    #define RESET_STEP do { \
        step = (char4)((char)sign(v.x), (char)sign(v.y), \
                       (char)sign(v.z), 0); \
    } while (0)
    RESET_STEP;

    /* tmax = (tmaxX, tmaxY, tmaxZ) = values of t at which ray next
       crosses a voxel boundary in the respective direction.
       In RESET_TMAX, the "step.dim ?" conditionalisation is because we want
       infinity, not NaN, when v.dim is 0. NaN would cause an infinite
       loop in portal rendering. */
    float4 tmax;
    #define RESET_TMAX do { \
        tmax = (float4)( \
            step.x ? (r.P.x + POSITIVE(step.x) - u.x)/v.x : 1.0/0.0, \
            step.y ? (r.P.y + POSITIVE(step.y) - u.y)/v.y : 1.0/0.0, \
            step.z ? (r.P.z + POSITIVE(step.z) - u.z)/v.z : 1.0/0.0, \
            0.0f); \
    } while (0)
    RESET_TMAX;

    /* dt = (dtX, dtY, dtZ) = how far along ray (in units of t) we must
       move for x/y/z component of move to equal width of one voxel */
    float4 dt;
    #define RESET_DT do { \
        dt = (float4)(step.x / v.x, step.y / v.y, step.z / v.z, 0.0f); \
    } while (0);
    RESET_DT;

    /* justOut vector - used for checking we remain within the bounds
       of the grid */
    int4 justOut;
    #define RESET_JUSTOUT do { \
        justOut = (int4)(step.x == -1 ? -1 : w.bounds.x, \
                         step.y == -1 ? -1 : w.bounds.y, \
                         step.z == -1 ? -1 : w.bounds.z, \
                         0); \
    } while (0)
    RESET_JUSTOUT;

    r.ray_color = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
    r.outside = false;
    r.last_step = (char4)(0,0,0,0);

    // FIXME assuming camera is always inside the grid
    r.ray_color = color_ray(w, r, v, t);

    #define STEP(dim) do { \
                        r.P.dim += step.dim; \
                        r.last_step = (char4)(0,0,0,0); \
                        r.last_step.dim = step.dim; \
                        t = tmax.dim; \
                        if (r.P.dim == justOut.dim) { \
                            r.outside = true; \
                        } else \
                        tmax.dim += dt.dim; \
                      } while (0)

    for (uint iter_count = 0; iter_count < LOOP_LIMIT;
         iter_count++)
    {

        if (tmax.x < tmax.y) {
            if (tmax.x < tmax.z) {
                STEP(x);
            } else {
                STEP(z);
            }
        } else {
            if (tmax.y < tmax.z) {
                STEP(y);
            } else {
                STEP(z);
            }
        }

        char portal_num = -1;
        if (!r.outside &&
            (portal_num = is_portal_block(grid_get(w, r.P))) != -1)
        {
            float16 portal = get_portal(w, portal_num);

            // Step to the opposite (back) face of the portal
            char steps = 1;
            r.P = vec_add_i4_c4(r.P, r.last_step);
            while (r.P.x != justOut.x &&
                r.P.y != justOut.y &&
                r.P.z != justOut.z &&
                is_portal_block(grid_get(w, r.P)) != -1)
            {
                r.P = vec_add_i4_c4(r.P, r.last_step);
                steps ++;
            }
            u = vec_add_f4_c4(u, vec_scale_c_c4(steps, r.last_step));

            // Now apply the portal transformation
            u = apply_matrix(portal, u);
            v = apply_matrix(portal, v);
            r.P = (int4)((int)(u.x + v.x*t),
                         (int)(u.y + v.y*t),
                         (int)(u.z + v.z*t),
                         1);
            RESET_STEP;
            RESET_DT;
            RESET_TMAX;

            RESET_JUSTOUT;

            /*if (r.P.x < 0 || r.P.x >= w.bounds.x ||
                r.P.y < 0 || r.P.y >= w.bounds.y ||
                r.P.z < 0 || r.P.z >= w.bounds.z)
            {
                r.ray_color = (float4)(0.0,1.0,0.0,1.0);
            }*/

        } else {
            r.ray_color = color_ray(w, r, v, t);
        }

        if (r.ray_color.w == 1.0f)
            break;

    }

    write_imagef(bmp, p_pos, r.ray_color);
}


fixed bug in MSB CUP

fixed bug in MSB CUP

/*  Copyright 2014 Aaron Boxer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "ocl_platform.cl"

/*

Bit Plane Coder
===============

A. Scan Pattern

Xx4 stripes

The stripes are scanned from top to bottom, and the columns within a
stripe are scanned from left to right.

*/

#define CODEBLOCKY_QUARTER 8

#define BOUNDARY 1
#define BOUNDARY_X2 2

#define CODEBLOCKY_X4 128

#define STATE_BUFFER_SIZE 1156
#define STATE_BOTTOM_BOUNDARY_OFFSET 1124

#define STATE_BUFFER_STRIDE 34
#define STATE_BUFFER_STRIDE_X2 68
#define STATE_BUFFER_STRIDE_X3 102
#define STATE_BUFFER_STRIDE_X4 136

#define LEFT_TOP -35
#define TOP  -34
#define RIGHT_TOP -33

#define LEFT   -1
#define RIGHT   1

#define LEFT_BOTTOM  33
#define BOTTOM  34
#define RIGHT_BOTTOM  35

// bit positions (0 based indices)
#define INPUT_SIGN_BITPOS  15

#define SIGMA_NEW_BITPOS   0x1
#define SIGMA_OLD_BITPOS   0x1
#define NBH_BITPOS         0x2
#define SIGMA_OLD_TO_NBH_SHIFT 0x1
#define RLC_BITPOS         0x4
#define PIXEL_START_BITPOS 0xA
#define PIXEL_END_BITPOS   0x18
#define SIGN_BITPOS        0x19

// bit flags
#define SIGMA_NEW_F			 0x1		  //position 0
#define NOT_SIGMA_NEW_F      0xFFFFFFFE   // ~SIGMA_NEW
#define SIGMA_OLD_F			 0x10		  //position  1
#define NBH_F				 0x20		  //position  2
#define RLC_F				 0x80		  //position  4
#define RLC_D_POSITION_F	 0x380        //positions 6-9
#define PIXEL_F				 0x7FFF0000   //positions 10-24
#define SIGN_F				 0x2000000    //position  25

#define INPUT_TO_SIGN_SHIFT 10

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE  | CLK_FILTER_NEAREST;

#define BIT(pix) (((pix)>>(bp))&1)
#define NBH(pix) ((pix) & NBH_F)
#define SIGMA_OLD(pix) ((pix) & SIGMA_OLD_F)
#define SIGMA_NEW(pix) ((pix) & SIGMA_NEW_F)
#define SIGN(pix) ((pix) & SIGN_F)

#define SET_SIGMA_NEW(pix) ( (pix) |= SIGMA_NEW_F )
#define CLEAR_SIGMA_NEW(pix) ( (pix) &= NOT_SIGMA_NEW_F )

#define AMD

/**

**/

void KERNEL run(read_only image2d_t channel) {

	// state buffer
	LOCAL int state[STATE_BUFFER_SIZE];
	LOCAL int cxd[STATE_BUFFER_SIZE];

	///////////////////////////////////////////////////////////////////////////////////
	//1. Calculate MSB

	LOCAL int msbScratch[CODEBLOCKX]; // between -1 and 31 - negative one value indicates that this code block is identically zero

	int maxSigBit;
	if (getLocalId(1) == 0) {

		int maxVal = 0;
		state[getLocalId(0)] = 0;   //top boundary
		LOCAL int* statePtr = state + (BOUNDARY + getLocalId(0));
		int2 posIn = (int2)(getGlobalId(0),  (getGlobalId(1) >> 3)*CODEBLOCKY);

		for (int i = 0; i < CODEBLOCKY; ++i) {
			int pixel = read_imagei(channel, sampler, posIn).x;
			int absPixel = abs(pixel);
			maxVal = max(maxVal, absPixel);
			pixel = (absPixel << PIXEL_START_BITPOS) | SIGN((pixel << INPUT_TO_SIGN_SHIFT));
			statePtr[0] = pixel;

			posIn.y++;
			statePtr += STATE_BUFFER_STRIDE;
		}
		state[ getLocalId(0) + STATE_BOTTOM_BOUNDARY_OFFSET] = 0;		//bottom boundary

		//initialize full left and right boundary columns
		if (getLocalId(0) == 0 || getLocalId(0) == CODEBLOCKX-1) {
			int delta = -1 + (getLocalId(0)/(CODEBLOCKX-1))*2; // -1 or +1
			statePtr = state + BOUNDARY + getLocalId(0) + delta;
			for (int i = 0; i < CODEBLOCKY+ BOUNDARY_X2; ++i) {
				 *statePtr = 0;
				 statePtr += STATE_BUFFER_STRIDE;
			}
		}

		// calculate column-msb
		maxSigBit = 31 - clz(maxVal);
		msbScratch[getLocalId(0)] =maxSigBit;
	}
	localMemoryFence();

	//calculate global msb
	if (!getLocalId(0) && !getLocalId(1) ) {
		int4 mx = (int4)(maxSigBit);

		LOCAL int* scratchPtr = msbScratch;
		for(int i=0; i < CODEBLOCKX; i+=4) {
			mx = max(mx,(int4)(msbScratch[i],msbScratch[i+1],msbScratch[i+2],msbScratch[i+3]));
		}
		maxSigBit = mx.x;
		maxSigBit = max(maxSigBit, mx.y);
		maxSigBit = max(maxSigBit, mx.z);
		maxSigBit = max(maxSigBit, mx.w);
		msbScratch[0] = maxSigBit;
	}
	localMemoryFence();

	if (msbScratch[0] == -1)
		return;

	int startIndex = (BOUNDARY + getLocalId(0)) + (BOUNDARY + getLocalId(1))*STATE_BUFFER_STRIDE_X4;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// 2. CUP on MSB

	int bp = msbScratch[0] + PIXEL_START_BITPOS;

	// i) set sigma_new for strip column (sigma_old is zero)
	LOCAL int* statePtr = state + startIndex;
	for (int i = 0; i < 4; ++i) {
		int current = statePtr[0];
		current |= BIT(current);
		*statePtr = current;
		statePtr += STATE_BUFFER_STRIDE;

	}
	localMemoryFence();

	// ii) set nbh for strip column, and do CUP
	statePtr = state + startIndex;
	int current			= statePtr[0];
	int top				= statePtr[TOP];
	int leftTop			= statePtr[LEFT_TOP];
	int left			= statePtr[LEFT];
	int leftBottom		= statePtr[LEFT_BOTTOM];

	int rlcCount = 0;

	// first pixel in strip column
	current |=  (BIT(top) | BIT(leftTop) | BIT(left) | BIT(leftBottom)) << NBH_BITPOS;
	statePtr[0] = current;
	bool doRLC = !NBH(current) && !BIT(current);
	if (doRLC) {
		rlcCount++;
	} else {
		//ZC

		if (BIT(current)) {
		    //SC
		}
	}

	// next two pixels in strip column
	for (int i = 0; i < 2; ++i) {

		statePtr += STATE_BUFFER_STRIDE;
		top = current;
		current		= statePtr[0];
		leftTop		= left;
		left		= leftBottom;
		leftBottom	= statePtr[LEFT_BOTTOM];

		current |=  (BIT(top) | BIT(leftTop) | BIT(left) | BIT(leftBottom)) << NBH_BITPOS;
		statePtr[0] = current;

		// toggle doRLC flag
		doRLC = doRLC && !NBH(current) && !BIT(current);
		if (doRLC) {

			rlcCount++;
		} else {
		   //ZC

		   if (BIT(current)) {
		       //SC
		   }
		}
	}

	// last pixel in strip column -
	// ignore leftBottom pixel, because it is in the next
	// stripe and it hasn't been processed yet
	statePtr += STATE_BUFFER_STRIDE;
	top			= current;
	current		= statePtr[0];
	leftTop		= left;
	left		= leftBottom;
	leftBottom	= statePtr[LEFT_BOTTOM];

	current |=  (BIT(top) | BIT(leftTop) | BIT(left)) << NBH_BITPOS;
	statePtr[0] = current;

	doRLC = doRLC && !NBH(current) && !BIT(current);
	if (doRLC) {

		rlcCount++;
	} else {
		//ZC

		if (BIT(current)) {
		    //SC
		}
	}

	localMemoryFence();

	/////////////////////////////////////////////////////////////////////////////////////////////////
	// 3. Processs rest of bit plans
	bp--;
	LOCAL char blockVote;

	while (bp >= PIXEL_START_BITPOS) {
		blockVote = 0;

		/////////////////////////////
		// 4. pre-process bit plane

		// i) migrate sigma_new to sigma_old, and clear sigma new bit
		statePtr = state + startIndex;
		for (int i = 0; i < 4; ++i) {
		    int current = *statePtr;
			current |=  SIGMA_NEW(current) << SIGMA_OLD_BITPOS;
			CLEAR_SIGMA_NEW(current);

			*statePtr = current;
			statePtr += STATE_BUFFER_STRIDE;

		}
		localMemoryFence();

		// ii) update nbh
		statePtr = state + startIndex;

		int top			= statePtr[TOP];
		int leftTop		= statePtr[LEFT_TOP];
		int rightTop	= statePtr[RIGHT_TOP];
		int left		= statePtr[LEFT];
		int current		= statePtr[0];
		int right		= statePtr[RIGHT];
		int leftBottom  = statePtr[LEFT_BOTTOM];
		int bottom		= statePtr[BOTTOM];
		int rightBottom = statePtr[RIGHT_BOTTOM];

		current |=  (   SIGMA_OLD(leftTop) |
						SIGMA_OLD( top) |
						SIGMA_OLD( rightTop) |
						SIGMA_OLD( left) |
						SIGMA_OLD( right) |
						SIGMA_OLD( leftBottom) |
						SIGMA_OLD( bottom) |
						SIGMA_OLD( rightBottom)  ) << SIGMA_OLD_TO_NBH_SHIFT;

		if ( BIT(current) &&
				NBH(current) &&
					!SIGMA_OLD(current) ) {
			SET_SIGMA_NEW(current);
			blockVote = 1;

		}
		*statePtr = current;

		for (int i = 0; i < 3; ++i) {
			statePtr += STATE_BUFFER_STRIDE;

			top			 = current;
			leftTop		 = left;
			rightTop	 = right;
			left		 = leftBottom;
			current		 = statePtr[0];
			right		 = rightBottom;
			leftBottom   = statePtr[LEFT_BOTTOM];
			bottom		 = statePtr[BOTTOM];
			rightBottom  = statePtr[RIGHT_BOTTOM];

			current |=	   ( SIGMA_OLD(leftTop) |
							 SIGMA_OLD( top) |
							 SIGMA_OLD( rightTop) |
							 SIGMA_OLD( left) |
							 SIGMA_OLD( right) |
							 SIGMA_OLD( leftBottom) |
							 SIGMA_OLD( bottom) |
							 SIGMA_OLD( rightBottom)  ) << SIGMA_OLD_TO_NBH_SHIFT;

			if ( BIT(current) &&
					 NBH(current) &&
						 !SIGMA_OLD(current) ) {
			    SET_SIGMA_NEW(current);
				blockVote = 1;
			}
			*statePtr = current;

		}
		localMemoryFence();

		// iii) block vote on sigma new
#ifdef AMD
		while (blockVote)
#endif
		{
		    blockVote = 0;
			localMemoryFence();

			statePtr = state + startIndex;

			int top			= statePtr[TOP];
			int leftTop		= statePtr[LEFT_TOP];
			int left		= statePtr[LEFT];
			int current		= statePtr[0];
			int leftBottom  = statePtr[LEFT_BOTTOM];

			// first three pixels in strip column
			for (int i = 0; i < 3; ++i) {

				current  |= ( SIGMA_NEW(leftTop) |
							  SIGMA_NEW( top) |
							  SIGMA_NEW( left) |
							  SIGMA_NEW( leftBottom) ) << NBH_BITPOS;

				if (  BIT(current) &&
				          !SIGMA_OLD(current) &&
						      NBH(current)  &&
							     !SIGMA_NEW(current)) {
					SET_SIGMA_NEW(current);
					blockVote = 1;

				}
				statePtr[0] = current;
				statePtr += STATE_BUFFER_STRIDE;

				top			= current;
				current		= statePtr[0];
				leftTop		= left;
				left		= leftBottom;
				leftBottom  = statePtr[LEFT_BOTTOM];
			}

			// last pixel in strip column -
			// ignore leftBottom pixel, because it is in the next
			// stripe and it hasn't been processed yet
			current |= ( SIGMA_NEW(leftTop) |
						 SIGMA_NEW( top) |
						 SIGMA_NEW( left) ) << NBH_BITPOS;
			if (  BIT(current) &&
			        !SIGMA_OLD(current) &&
					    NBH(current)  &&
						    !SIGMA_NEW(current)) {
				SET_SIGMA_NEW(current);
				blockVote = 1;
			}
			statePtr[0] = current;

			localMemoryFence();

		}

		/////////////////////////
		// 5. bit plane processing
		statePtr = state + startIndex;

		top = statePtr[TOP];
		leftTop			= statePtr[LEFT_TOP];
		rightTop		= statePtr[RIGHT_TOP];
		left			= statePtr[LEFT];
		current			= statePtr[0];
		right			= statePtr[RIGHT];
		leftBottom		= statePtr[LEFT_BOTTOM];
		bottom			= statePtr[BOTTOM];
		rightBottom		= statePtr[RIGHT_BOTTOM];

		if (SIGMA_OLD(current)) {
			// MRC
		} else {
			int nbh = ( NBH(leftTop) |
						NBH( top) |
						NBH( rightTop) |
						NBH( left) |
						NBH( right) |
						NBH( leftBottom) |
						NBH( bottom) |
						NBH( rightBottom)  ) ;

			if (!nbh) {
				if ( !SIGMA_NEW(current) ) {
					//RLC
				}
			}
			else  {
				// ZC
				if (BIT(current)) {
				    //SC
				}
			}
		}

		for (int i = 0; i < 3; ++i) {
			statePtr += STATE_BUFFER_STRIDE;

			top			= current;
			leftTop		= left;
			rightTop	= right;
			left		= leftBottom;
			current		= statePtr[0];
			right		= rightBottom;
			leftBottom  = statePtr[LEFT_BOTTOM];
			bottom		= statePtr[BOTTOM];
			rightBottom = statePtr[RIGHT_BOTTOM];

			if (SIGMA_OLD(current)) {
				// MRC
			} else {
				int nbh = ( NBH(leftTop) |
							NBH( top) |
							NBH( rightTop) |
							NBH( left) |
							NBH( right) |
							NBH( leftBottom) |
							NBH( bottom) |
							NBH( rightBottom)  ) ;

				if (!nbh) {
					if ( !SIGMA_NEW(current) ) {
						//RLC
					}
				}
				else  {
					// ZC
					if (BIT(current)) {
						//SC
					}
				}
			}
		}

		localMemoryFence();
		bp--;
	}
}

/*  Copyright 2014 Aaron Boxer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "ocl_platform.cl"

/*

Bit Plane Coder
===============

A. Scan Pattern

Xx4 stripes

The stripes are scanned from top to bottom, and the columns within a
stripe are scanned from left to right.

*/

#define CODEBLOCKY_QUARTER 8

#define BOUNDARY 1
#define BOUNDARY_X2 2

#define CODEBLOCKY_X4 128

#define STATE_BUFFER_SIZE 1156
#define STATE_BOTTOM_BOUNDARY_OFFSET 1124

#define STATE_BUFFER_STRIDE 34
#define STATE_BUFFER_STRIDE_X2 68
#define STATE_BUFFER_STRIDE_X3 102
#define STATE_BUFFER_STRIDE_X4 136

#define LEFT_TOP -35
#define TOP  -34
#define RIGHT_TOP -33

#define LEFT   -1
#define RIGHT   1

#define LEFT_BOTTOM  33
#define BOTTOM  34
#define RIGHT_BOTTOM  35

// bit positions (0 based indices)
#define INPUT_SIGN_BITPOS  15

#define SIGMA_NEW_BITPOS   0x1
#define SIGMA_OLD_BITPOS   0x1
#define NBH_BITPOS         0x2
#define SIGMA_OLD_TO_NBH_SHIFT 0x1
#define RLC_BITPOS         0x4
#define PIXEL_START_BITPOS 0xA
#define PIXEL_END_BITPOS   0x18
#define SIGN_BITPOS        0x19

// bit flags
#define SIGMA_NEW_F			 0x1		  //position 0
#define NOT_SIGMA_NEW_F      0xFFFFFFFE   // ~SIGMA_NEW
#define SIGMA_OLD_F			 0x10		  //position  1
#define NBH_F				 0x20		  //position  2
#define RLC_F				 0x80		  //position  4
#define RLC_D_POSITION_F	 0x380        //positions 6-9
#define PIXEL_F				 0x7FFF0000   //positions 10-24
#define SIGN_F				 0x2000000    //position  25

#define INPUT_TO_SIGN_SHIFT 10

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE  | CLK_FILTER_NEAREST;

#define BIT(pix) (((pix)>>(bp))&1)
#define NBH(pix) ((pix) & NBH_F)
#define SIGMA_OLD(pix) ((pix) & SIGMA_OLD_F)
#define SIGMA_NEW(pix) ((pix) & SIGMA_NEW_F)
#define SIGN(pix) ((pix) & SIGN_F)

#define SET_SIGMA_NEW(pix) ( (pix) |= SIGMA_NEW_F )
#define CLEAR_SIGMA_NEW(pix) ( (pix) &= NOT_SIGMA_NEW_F )

#define AMD

/**

**/

void KERNEL run(read_only image2d_t channel) {

	// state buffer
	LOCAL int state[STATE_BUFFER_SIZE];
	LOCAL int cxd[STATE_BUFFER_SIZE];

	///////////////////////////////////////////////////////////////////////////////////
	//1. Calculate MSB

	LOCAL int msbScratch[CODEBLOCKX]; // between one and 32 - zero value indicates that this code block is identically zero

	int maxSigBit;
	if (getLocalId(1) == 0) {

		int maxVal = 0;
		state[getLocalId(0)] = 0;   //top boundary
		LOCAL int* statePtr = state + (BOUNDARY + getLocalId(0));
		int2 posIn = (int2)(getGlobalId(0),  (getGlobalId(1) >> 3)*CODEBLOCKY);

		for (int i = 0; i < CODEBLOCKY; ++i) {
			int pixel = read_imagei(channel, sampler, posIn).x;
			int absPixel = abs(pixel);
			maxVal = max(maxVal, absPixel);
			pixel = (absPixel << PIXEL_START_BITPOS) | SIGN((pixel << INPUT_TO_SIGN_SHIFT));
			statePtr[0] = pixel;

			posIn.y++;
			statePtr += STATE_BUFFER_STRIDE;
		}
		state[ getLocalId(0) + STATE_BOTTOM_BOUNDARY_OFFSET] = 0;		//bottom boundary

		//initialize full left and right boundary columns
		if (getLocalId(0) == 0 || getLocalId(0) == CODEBLOCKX-1) {
			int delta = -1 + (getLocalId(0)/(CODEBLOCKX-1))*2; // -1 or +1
			statePtr = state + BOUNDARY + getLocalId(0) + delta;
			for (int i = 0; i < CODEBLOCKY+ BOUNDARY_X2; ++i) {
				 *statePtr = 0;
				 statePtr += STATE_BUFFER_STRIDE;
			}
		}

		// calculate column-msb
		maxSigBit = 31 - clz(maxVal);
		msbScratch[getLocalId(0)] =maxSigBit;
	}
	localMemoryFence();

	//calculate global msb
	if (!getLocalId(0) && !getLocalId(1) ) {
		int4 mx = (int4)(maxSigBit);

		LOCAL int* scratchPtr = msbScratch;
		for(int i=0; i < CODEBLOCKX; i+=4) {
			mx = max(mx,(int4)(msbScratch[i],msbScratch[i+1],msbScratch[i+2],msbScratch[i+3]));
		}
		maxSigBit = mx.x;
		maxSigBit = max(maxSigBit, mx.y);
		maxSigBit = max(maxSigBit, mx.z);
		maxSigBit = max(maxSigBit, mx.w);
		msbScratch[0] = maxSigBit;
	}
	localMemoryFence();

	if (msbScratch[0] == -1)
		return;

	int startIndex = (BOUNDARY + getLocalId(0)) + (BOUNDARY + getLocalId(1))*STATE_BUFFER_STRIDE_X4;

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 2. CUP on MSB

	//Algorithm 13 Clean-up pass on the MSB
	//1: set sigmanew := bit value
	//2: set rlcNbh :=  SUM(sigmanew) of preceding column and upper position
	//3: if rlcNbh = 0 and y-dimension of current position is a multiple of 4
	//then
	//4: execute RLC operation
	//5: end if
	//6: if rlcNbh = 1 then
	//7: execute ZC operation
	//8: end if
	//9: if sigmanew = 1 then
	//10: execute SC operation
	//11: end if

	int bp = msbScratch[0] + PIXEL_START_BITPOS;

	// i) set sigma_new for strip column (sigma_old is zero)
	LOCAL int* statePtr = state + startIndex;
	for (int i = 0; i < 4; ++i) {
		int current = statePtr[0];
		current |= BIT(current);
		*statePtr = current;
		statePtr += STATE_BUFFER_STRIDE;

	}
	localMemoryFence();

	// ii) set rlc nbh for strip column
	statePtr = state + startIndex;
	int current			= statePtr[0];
	int top				= statePtr[TOP];
	int leftTop			= statePtr[LEFT_TOP];
	int left			= statePtr[LEFT];
	int leftBottom		= statePtr[LEFT_BOTTOM];

	int rlcCount = 0;

	current |=  (BIT(top) | BIT(leftTop) | BIT(left) | BIT(leftBottom)) << NBH_BITPOS;
	statePtr[0] = current;

	// set doRLC flag
	bool doRLC = !NBH(current) && !BIT(current);

	if (doRLC) {
		rlcCount++;
	} else {
		if ( NBH(current) ) {
			//ZC
		}
		if (BIT(current)) {
		    //SC
		}
	}

	for (int i = 0; i < 2; ++i) {

		statePtr += STATE_BUFFER_STRIDE;
		top = current;
		current = statePtr[0];
		leftTop = left;
		left = leftBottom;
		leftBottom = statePtr[LEFT_BOTTOM];
		current |=  (BIT(top) | BIT(leftTop) | BIT(left) | BIT(leftBottom)) << NBH_BITPOS;
		statePtr[0] = current;

		// toggle doRLC flag
		doRLC = doRLC && !NBH(current) && !BIT(current);
		if (doRLC) {

			rlcCount++;
		} else {
		   if (NBH(current)) {
				//ZC
		   }
		   if (BIT(current)) {
		       //SC
		   }
		}
	}

	// last pixel in strip column -
	// ignore leftBottom pixel, because it is in the next
	// stripe and it hasn't been processed yet
	statePtr += STATE_BUFFER_STRIDE;
	top = current;
	current = statePtr[0];
	leftTop = left;
	left = leftBottom;
	leftBottom = statePtr[LEFT_BOTTOM];
	current |=  (BIT(top) | BIT(leftTop) | BIT(left)) << NBH_BITPOS;
	statePtr[0] = current;

	// toggle doRLC flag
	doRLC = doRLC && !NBH(current) && !BIT(current);
	if (doRLC) {

		rlcCount++;
	} else {
		if (NBH(current)) {
			//ZC
		}
		if (BIT(current)) {
		    //SC
		}
	}

	localMemoryFence();

	/////////////////////////////////////////////////////////////////////////////////////////////////
	// 3. Processs rest of bit plans
	bp--;
	LOCAL char blockVote;

	while (bp >= PIXEL_START_BITPOS) {
		blockVote = 0;

		/////////////////////////////
		// 4. pre-process bit plane

		// i) migrate sigma_new to sigma_old, and clear sigma new bit
		statePtr = state + startIndex;
		for (int i = 0; i < 4; ++i) {
		    int current = *statePtr;
			current |=  SIGMA_NEW(current) << SIGMA_OLD_BITPOS;
			CLEAR_SIGMA_NEW(current);

			*statePtr = current;
			statePtr += STATE_BUFFER_STRIDE;

		}
		localMemoryFence();

		// ii) update nbh
		statePtr = state + startIndex;

		int top = statePtr[TOP];
		int leftTop = statePtr[LEFT_TOP];
		int rightTop = statePtr[RIGHT_TOP];
		int left = statePtr[LEFT];
		int current = statePtr[0];
		int right = statePtr[RIGHT];
		int leftBottom = statePtr[LEFT_BOTTOM];
		int bottom = statePtr[BOTTOM];
		int rightBottom = statePtr[RIGHT_BOTTOM];

		current |=  (   SIGMA_OLD(leftTop) |
						SIGMA_OLD( top) |
						SIGMA_OLD( rightTop) |
						SIGMA_OLD( left) |
						SIGMA_OLD( right) |
						SIGMA_OLD( leftBottom) |
						SIGMA_OLD( bottom) |
						SIGMA_OLD( rightBottom)  ) << SIGMA_OLD_TO_NBH_SHIFT;

		if ( BIT(current) &&
				NBH(current) &&
					!SIGMA_OLD(current) ) {
			SET_SIGMA_NEW(current);
			blockVote = 1;

		}
		*statePtr = current;

		for (int i = 0; i < 3; ++i) {
			statePtr += STATE_BUFFER_STRIDE;

			top			 = current;
			leftTop		 = left;
			rightTop	 = right;
			left		 = leftBottom;
			current		 = statePtr[0];
			right		 = rightBottom;
			leftBottom   = statePtr[LEFT_BOTTOM];
			bottom		 = statePtr[BOTTOM];
			rightBottom  = statePtr[RIGHT_BOTTOM];

			current |=	   ( SIGMA_OLD(leftTop) |
							 SIGMA_OLD( top) |
							 SIGMA_OLD( rightTop) |
							 SIGMA_OLD( left) |
							 SIGMA_OLD( right) |
							 SIGMA_OLD( leftBottom) |
							 SIGMA_OLD( bottom) |
							 SIGMA_OLD( rightBottom)  ) << SIGMA_OLD_TO_NBH_SHIFT;

			if ( BIT(current) &&
					 NBH(current) &&
						 !SIGMA_OLD(current) ) {
			    SET_SIGMA_NEW(current);
				blockVote = 1;
			}
			*statePtr = current;

		}
		localMemoryFence();

		// iii) block vote on sigma new
#ifdef AMD
		while (blockVote)
#endif
		{
		    blockVote = 0;
			localMemoryFence();

			statePtr = state + startIndex;

			int top			= statePtr[TOP];
			int leftTop		= statePtr[LEFT_TOP];
			int left		= statePtr[LEFT];
			int current		= statePtr[0];
			int leftBottom  = statePtr[LEFT_BOTTOM];

			// first three pixels in strip column
			for (int i = 0; i < 3; ++i) {

				current  |= ( SIGMA_NEW(leftTop) |
							  SIGMA_NEW( top) |
							  SIGMA_NEW( left) |
							  SIGMA_NEW( leftBottom) ) << NBH_BITPOS;

				if (  BIT(current) &&
				          !SIGMA_OLD(current) &&
						      NBH(current)  &&
							     !SIGMA_NEW(current)) {
					SET_SIGMA_NEW(current);
					blockVote = 1;

				}
				statePtr[0] = current;
				statePtr += STATE_BUFFER_STRIDE;

				top			= current;
				current		= statePtr[0];
				leftTop		= left;
				left		= leftBottom;
				leftBottom  = statePtr[LEFT_BOTTOM];
			}

			// last pixel in strip column -
			// ignore leftBottom pixel, because it is in the next
			// stripe and it hasn't been processed yet
			current |= ( SIGMA_NEW(leftTop) |
						 SIGMA_NEW( top) |
						 SIGMA_NEW( left) ) << NBH_BITPOS;
			if (  BIT(current) &&
			        !SIGMA_OLD(current) &&
					    NBH(current)  &&
						    !SIGMA_NEW(current)) {
				SET_SIGMA_NEW(current);
				blockVote = 1;
			}
			statePtr[0] = current;

			localMemoryFence();

		}

		/////////////////////////
		// 5. bit plane processing
		statePtr = state + startIndex;

		top = statePtr[TOP];
		leftTop			= statePtr[LEFT_TOP];
		rightTop		= statePtr[RIGHT_TOP];
		left			= statePtr[LEFT];
		current			= statePtr[0];
		right			= statePtr[RIGHT];
		leftBottom		= statePtr[LEFT_BOTTOM];
		bottom			= statePtr[BOTTOM];
		rightBottom		= statePtr[RIGHT_BOTTOM];

		if (SIGMA_OLD(current)) {
			// MRC
		} else {
			int nbh = ( NBH(leftTop) |
						NBH( top) |
						NBH( rightTop) |
						NBH( left) |
						NBH( right) |
						NBH( leftBottom) |
						NBH( bottom) |
						NBH( rightBottom)  ) ;

			if (!nbh) {
				if ( !SIGMA_NEW(current) ) {

					//RLC
				}
			}
			else  {

				 // ZC

				 if (BIT(current)) {
				     //SC
				  }
			}
		}

		for (int i = 0; i < 3; ++i) {
			statePtr += STATE_BUFFER_STRIDE;

			top			= current;
			leftTop		= left;
			rightTop	= right;
			left		= leftBottom;
			current		= statePtr[0];
			right		= rightBottom;
			leftBottom  = statePtr[LEFT_BOTTOM];
			bottom		= statePtr[BOTTOM];
			rightBottom = statePtr[RIGHT_BOTTOM];

			if (SIGMA_OLD(current)) {
				// MRC
			} else {
				int nbh = ( NBH(leftTop) |
							NBH( top) |
							NBH( rightTop) |
							NBH( left) |
							NBH( right) |
							NBH( leftBottom) |
							NBH( bottom) |
							NBH( rightBottom)  ) ;

				if (!nbh) {
					if ( !SIGMA_NEW(current) ) {
						//RLC
					}
				}
				else  {
					 // ZC
					 if (BIT(current)) {
						 //SC
					  }
				}
			}
		}

		localMemoryFence();
		bp--;
	}
}