[OpenCL] Remove unwanted signedness conversion from tests

The get_kernel_* functions used in cl20-device-side-enqueue.cl all return
unsigned integers. This patch avoids undesired implicit conversions on the
returned values.

Differential Revision: https://reviews.llvm.org/D52873

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

// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple "spir-unknown-unknown" -verify -pedantic -fsyntax-only -DB32 -DQUALS=
// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple "spir-unknown-unknown" -verify -pedantic -fsyntax-only -DB32 -DQUALS="const volatile"
// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple "spir64-unknown-unknown" -verify -pedantic -fsyntax-only -Wconversion -DWCONV -DQUALS=
// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple "spir64-unknown-unknown" -verify -pedantic -fsyntax-only -Wconversion -DWCONV -DQUALS="const volatile"

typedef struct {int a;} ndrange_t;
// Diagnostic tests for different overloads of enqueue_kernel from Table 6.13.17.1 of OpenCL 2.0 Spec.
kernel void enqueue_kernel_tests() {
  queue_t default_queue;
  unsigned flags = 0;
  QUALS ndrange_t ndrange;
  clk_event_t evt;
  clk_event_t event_wait_list;
  clk_event_t event_wait_list2[] = {evt, evt};
  void *vptr;

  // Testing the first overload type
  enqueue_kernel(default_queue, flags, ndrange, ^(void) {
    return 0;
  });

  enqueue_kernel(vptr, flags, ndrange, ^(void) { // expected-error{{illegal call to 'enqueue_kernel', expected 'queue_t' argument type}}
    return 0;
  });

  enqueue_kernel(default_queue, vptr, ndrange, ^(void) { // expected-error{{illegal call to 'enqueue_kernel', expected 'kernel_enqueue_flags_t' (i.e. uint) argument type}}
    return 0;
  });

  enqueue_kernel(default_queue, flags, vptr, ^(void) { // expected-error{{illegal call to 'enqueue_kernel', expected 'ndrange_t' argument type}}
    return 0;
  });

  enqueue_kernel(default_queue, flags, ndrange, vptr); // expected-error{{illegal call to 'enqueue_kernel', expected block argument}}

  enqueue_kernel(default_queue, flags, ndrange, ^(int i) { // expected-error{{blocks with parameters are not accepted in this prototype of enqueue_kernel call}}
    return 0;
  });

  // Testing the second overload type
  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, &evt, ^(void) {
                                                                             return 0;
                                                                           });

  enqueue_kernel(default_queue, flags, ndrange, 1, 0, 0, ^(void) {
                                                           return 0;
                                                         });

  enqueue_kernel(default_queue, flags, ndrange, vptr, &event_wait_list, &evt, ^(void) { // expected-error{{illegal call to 'enqueue_kernel', expected integer argument type}}
    return 0;
  });

  enqueue_kernel(default_queue, flags, ndrange, 1, vptr, &evt, ^(void) // expected-error{{illegal call to 'enqueue_kernel', expected 'clk_event_t *' argument type}}
                                                               {
                                                                 return 0;
                                                               });

  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, vptr, ^(void) // expected-error{{illegal call to 'enqueue_kernel', expected 'clk_event_t *' argument type}}
                                                                           {
                                                                             return 0;
                                                                           });

  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, &evt, vptr); // expected-error{{illegal call to 'enqueue_kernel', expected block argument}}

  // Testing the third overload type
  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 1024);

  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024L, 1024);

  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 4294967296L);
#ifdef B32
// expected-warning@-2{{implicit conversion from 'long' to 'unsigned int' changes value from 4294967296 to 0}}
#endif

  char c;
  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 c, 1024L);
#ifdef WCONV
// expected-warning-re@-2{{implicit conversion changes signedness: 'char' to 'unsigned {{int|long}}'}}
#endif
#define UINT_MAX 4294967295

  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 sizeof(int), sizeof(int) * UINT_MAX);
#ifdef B32
// expected-warning@-2{{implicit conversion from 'long' to 'unsigned int' changes value from 17179869180 to 4294967292}}
#endif

  typedef void (^bl_A_t)(local void *);

  const bl_A_t block_A = (bl_A_t) ^ (local void *a) {};

  enqueue_kernel(default_queue, flags, ndrange, block_A, 1024);

  typedef void (^bl_B_t)(local void *, local int *);

  const bl_B_t block_B = (bl_B_t) ^ (local void *a, local int *b) {};

  enqueue_kernel(default_queue, flags, ndrange, block_B, 1024, 1024); // expected-error{{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}

  enqueue_kernel(default_queue, flags, ndrange, // expected-error{{mismatch in number of block parameters and local size arguments passed}}
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024);

  float illegal_mem_size = (float)0.5f;
  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 illegal_mem_size, illegal_mem_size); // expected-error{{illegal call to enqueue_kernel, parameter needs to be specified as integer type}} expected-error{{illegal call to enqueue_kernel, parameter needs to be specified as integer type}}

  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 illegal_mem_size, 1024); // expected-error{{illegal call to enqueue_kernel, parameter needs to be specified as integer type}}

  // Testing the forth overload type
  enqueue_kernel(default_queue, flags, ndrange, 1, event_wait_list2, &evt,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 1024);

  enqueue_kernel(default_queue, flags, ndrange, 1, 0, 0,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 1024);

  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, &evt, // expected-error{{mismatch in number of block parameters and local size arguments passed}}
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 1024, 1024);

  // More random misc cases that can't be deduced
  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, &evt); // expected-error{{illegal call to enqueue_kernel, incorrect argument types}}

  enqueue_kernel(default_queue, flags, ndrange, 1, 1); // expected-error{{illegal call to enqueue_kernel, incorrect argument types}}
}

// Diagnostic tests for get_kernel_work_group_size and allowed block parameter types in dynamic parallelism.
kernel void work_group_size_tests() {
  void (^const block_A)(void) = ^{
    return;
  };
  void (^const block_B)(int) = ^(int a) {
    return;
  };
  void (^const block_C)(local void *) = ^(local void *a) {
    return;
  };
  void (^const block_D)(local int *) = ^(local int *a) {
    return;
  };

  unsigned size = get_kernel_work_group_size(block_A);
  size = get_kernel_work_group_size(block_C);
  size = get_kernel_work_group_size(^(local void *a) {
    return;
  });
  size = get_kernel_work_group_size(^(local int *a) { // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
    return;
  });
  size = get_kernel_work_group_size(block_B);   // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
  size = get_kernel_work_group_size(block_D);   // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
  size = get_kernel_work_group_size(^(int a) {  // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
    return;
  });
  size = get_kernel_work_group_size();          // expected-error {{too few arguments to function call, expected 1, have 0}}
  size = get_kernel_work_group_size(1);         // expected-error{{expected block argument}}
  size = get_kernel_work_group_size(block_A, 1); // expected-error{{too many arguments to function call, expected 1, have 2}}

  size = get_kernel_preferred_work_group_size_multiple(block_A);
  size = get_kernel_preferred_work_group_size_multiple(block_C);
  size = get_kernel_preferred_work_group_size_multiple(^(local void *a) {
    return;
  });
  size = get_kernel_preferred_work_group_size_multiple(^(local int *a) { // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
    return;
  });
  size = get_kernel_preferred_work_group_size_multiple(^(int a) {  // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
    return;
  });
  size = get_kernel_preferred_work_group_size_multiple(block_B);   // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
  size = get_kernel_preferred_work_group_size_multiple(block_D);   // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
  size = get_kernel_preferred_work_group_size_multiple();          // expected-error {{too few arguments to function call, expected 1, have 0}}
  size = get_kernel_preferred_work_group_size_multiple(1);         // expected-error{{expected block argument}}
  size = get_kernel_preferred_work_group_size_multiple(block_A, 1); // expected-error{{too many arguments to function call, expected 1, have 2}}
}

#pragma OPENCL EXTENSION cl_khr_subgroups : enable

kernel void foo(global unsigned int *buf)
{
  ndrange_t n;
  buf[0] = get_kernel_max_sub_group_size_for_ndrange(n, ^(){});
  buf[0] = get_kernel_max_sub_group_size_for_ndrange(0, ^(){}); // expected-error{{illegal call to 'get_kernel_max_sub_group_size_for_ndrange', expected 'ndrange_t' argument type}}
  buf[0] = get_kernel_max_sub_group_size_for_ndrange(n, 1); // expected-error{{illegal call to 'get_kernel_max_sub_group_size_for_ndrange', expected block argument type}}
}

kernel void bar(global unsigned int *buf)
{
  __private ndrange_t n;
  buf[0] = get_kernel_sub_group_count_for_ndrange(n, ^(){});
  buf[0] = get_kernel_sub_group_count_for_ndrange(0, ^(){}); // expected-error{{illegal call to 'get_kernel_sub_group_count_for_ndrange', expected 'ndrange_t' argument type}}
  buf[0] = get_kernel_sub_group_count_for_ndrange(n, 1); // expected-error{{illegal call to 'get_kernel_sub_group_count_for_ndrange', expected block argument type}}
}

#pragma OPENCL EXTENSION cl_khr_subgroups : disable

kernel void foo1(global unsigned int *buf)
{
  ndrange_t n;
  buf[0] = get_kernel_max_sub_group_size_for_ndrange(n, ^(){}); // expected-error {{use of declaration 'get_kernel_max_sub_group_size_for_ndrange' requires cl_khr_subgroups extension to be enabled}}
}

kernel void bar1(global unsigned int *buf)
{
  ndrange_t n;
  buf[0] = get_kernel_sub_group_count_for_ndrange(n, ^(){}); // expected-error {{use of declaration 'get_kernel_sub_group_count_for_ndrange' requires cl_khr_subgroups extension to be enabled}}
}

// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple "spir-unknown-unknown" -verify -pedantic -fsyntax-only -DB32 -DQUALS=
// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple "spir-unknown-unknown" -verify -pedantic -fsyntax-only -DB32 -DQUALS="const volatile"
// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple "spir64-unknown-unknown" -verify -pedantic -fsyntax-only -Wconversion -DWCONV -DQUALS=
// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple "spir64-unknown-unknown" -verify -pedantic -fsyntax-only -Wconversion -DWCONV -DQUALS="const volatile"

typedef struct {int a;} ndrange_t;
// Diagnostic tests for different overloads of enqueue_kernel from Table 6.13.17.1 of OpenCL 2.0 Spec.
kernel void enqueue_kernel_tests() {
  queue_t default_queue;
  unsigned flags = 0;
  QUALS ndrange_t ndrange;
  clk_event_t evt;
  clk_event_t event_wait_list;
  clk_event_t event_wait_list2[] = {evt, evt};
  void *vptr;

  // Testing the first overload type
  enqueue_kernel(default_queue, flags, ndrange, ^(void) {
    return 0;
  });

  enqueue_kernel(vptr, flags, ndrange, ^(void) { // expected-error{{illegal call to 'enqueue_kernel', expected 'queue_t' argument type}}
    return 0;
  });

  enqueue_kernel(default_queue, vptr, ndrange, ^(void) { // expected-error{{illegal call to 'enqueue_kernel', expected 'kernel_enqueue_flags_t' (i.e. uint) argument type}}
    return 0;
  });

  enqueue_kernel(default_queue, flags, vptr, ^(void) { // expected-error{{illegal call to 'enqueue_kernel', expected 'ndrange_t' argument type}}
    return 0;
  });

  enqueue_kernel(default_queue, flags, ndrange, vptr); // expected-error{{illegal call to 'enqueue_kernel', expected block argument}}

  enqueue_kernel(default_queue, flags, ndrange, ^(int i) { // expected-error{{blocks with parameters are not accepted in this prototype of enqueue_kernel call}}
    return 0;
  });

  // Testing the second overload type
  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, &evt, ^(void) {
                                                                             return 0;
                                                                           });

  enqueue_kernel(default_queue, flags, ndrange, 1, 0, 0, ^(void) {
                                                           return 0;
                                                         });

  enqueue_kernel(default_queue, flags, ndrange, vptr, &event_wait_list, &evt, ^(void) { // expected-error{{illegal call to 'enqueue_kernel', expected integer argument type}}
    return 0;
  });

  enqueue_kernel(default_queue, flags, ndrange, 1, vptr, &evt, ^(void) // expected-error{{illegal call to 'enqueue_kernel', expected 'clk_event_t *' argument type}}
                                                               {
                                                                 return 0;
                                                               });

  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, vptr, ^(void) // expected-error{{illegal call to 'enqueue_kernel', expected 'clk_event_t *' argument type}}
                                                                           {
                                                                             return 0;
                                                                           });

  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, &evt, vptr); // expected-error{{illegal call to 'enqueue_kernel', expected block argument}}

  // Testing the third overload type
  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 1024);

  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024L, 1024);

  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 4294967296L);
#ifdef B32
// expected-warning@-2{{implicit conversion from 'long' to 'unsigned int' changes value from 4294967296 to 0}}
#endif

  char c;
  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 c, 1024L);
#ifdef WCONV
// expected-warning-re@-2{{implicit conversion changes signedness: 'char' to 'unsigned {{int|long}}'}}
#endif
#define UINT_MAX 4294967295

  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 sizeof(int), sizeof(int) * UINT_MAX);
#ifdef B32
// expected-warning@-2{{implicit conversion from 'long' to 'unsigned int' changes value from 17179869180 to 4294967292}}
#endif

  typedef void (^bl_A_t)(local void *);

  const bl_A_t block_A = (bl_A_t) ^ (local void *a) {};

  enqueue_kernel(default_queue, flags, ndrange, block_A, 1024);

  typedef void (^bl_B_t)(local void *, local int *);

  const bl_B_t block_B = (bl_B_t) ^ (local void *a, local int *b) {};

  enqueue_kernel(default_queue, flags, ndrange, block_B, 1024, 1024); // expected-error{{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}

  enqueue_kernel(default_queue, flags, ndrange, // expected-error{{mismatch in number of block parameters and local size arguments passed}}
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024);

  float illegal_mem_size = (float)0.5f;
  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 illegal_mem_size, illegal_mem_size); // expected-error{{illegal call to enqueue_kernel, parameter needs to be specified as integer type}} expected-error{{illegal call to enqueue_kernel, parameter needs to be specified as integer type}}

  enqueue_kernel(default_queue, flags, ndrange,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 illegal_mem_size, 1024); // expected-error{{illegal call to enqueue_kernel, parameter needs to be specified as integer type}}

  // Testing the forth overload type
  enqueue_kernel(default_queue, flags, ndrange, 1, event_wait_list2, &evt,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 1024);

  enqueue_kernel(default_queue, flags, ndrange, 1, 0, 0,
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 1024);

  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, &evt, // expected-error{{mismatch in number of block parameters and local size arguments passed}}
                 ^(local void *a, local void *b) {
                   return 0;
                 },
                 1024, 1024, 1024);

  // More random misc cases that can't be deduced
  enqueue_kernel(default_queue, flags, ndrange, 1, &event_wait_list, &evt); // expected-error{{illegal call to enqueue_kernel, incorrect argument types}}

  enqueue_kernel(default_queue, flags, ndrange, 1, 1); // expected-error{{illegal call to enqueue_kernel, incorrect argument types}}
}

// Diagnostic tests for get_kernel_work_group_size and allowed block parameter types in dynamic parallelism.
kernel void work_group_size_tests() {
  void (^const block_A)(void) = ^{
    return;
  };
  void (^const block_B)(int) = ^(int a) {
    return;
  };
  void (^const block_C)(local void *) = ^(local void *a) {
    return;
  };
  void (^const block_D)(local int *) = ^(local int *a) {
    return;
  };

  unsigned size = get_kernel_work_group_size(block_A);
  size = get_kernel_work_group_size(block_C);
  size = get_kernel_work_group_size(^(local void *a) {
    return;
  });
  size = get_kernel_work_group_size(^(local int *a) { // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
    return;
  });
  size = get_kernel_work_group_size(block_B);   // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
  size = get_kernel_work_group_size(block_D);   // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
  size = get_kernel_work_group_size(^(int a) {  // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
    return;
  });
  size = get_kernel_work_group_size();          // expected-error {{too few arguments to function call, expected 1, have 0}}
  size = get_kernel_work_group_size(1);         // expected-error{{expected block argument}}
  size = get_kernel_work_group_size(block_A, 1); // expected-error{{too many arguments to function call, expected 1, have 2}}

  size = get_kernel_preferred_work_group_size_multiple(block_A);
  size = get_kernel_preferred_work_group_size_multiple(block_C);
  size = get_kernel_preferred_work_group_size_multiple(^(local void *a) {
    return;
  });
  size = get_kernel_preferred_work_group_size_multiple(^(local int *a) { // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
    return;
  });
  size = get_kernel_preferred_work_group_size_multiple(^(int a) {  // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
    return;
  });
  size = get_kernel_preferred_work_group_size_multiple(block_B);   // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
  size = get_kernel_preferred_work_group_size_multiple(block_D);   // expected-error {{blocks used in enqueue_kernel call are expected to have parameters of type 'local void*'}}
  size = get_kernel_preferred_work_group_size_multiple();          // expected-error {{too few arguments to function call, expected 1, have 0}}
  size = get_kernel_preferred_work_group_size_multiple(1);         // expected-error{{expected block argument}}
  size = get_kernel_preferred_work_group_size_multiple(block_A, 1); // expected-error{{too many arguments to function call, expected 1, have 2}}
}

#pragma OPENCL EXTENSION cl_khr_subgroups : enable

kernel void foo(global int *buf)
{
  ndrange_t n;
  buf[0] = get_kernel_max_sub_group_size_for_ndrange(n, ^(){});
  buf[0] = get_kernel_max_sub_group_size_for_ndrange(0, ^(){}); // expected-error{{illegal call to 'get_kernel_max_sub_group_size_for_ndrange', expected 'ndrange_t' argument type}}
  buf[0] = get_kernel_max_sub_group_size_for_ndrange(n, 1); // expected-error{{illegal call to 'get_kernel_max_sub_group_size_for_ndrange', expected block argument type}}
}

kernel void bar(global int *buf)
{
  __private ndrange_t n;
  buf[0] = get_kernel_sub_group_count_for_ndrange(n, ^(){});
  buf[0] = get_kernel_sub_group_count_for_ndrange(0, ^(){}); // expected-error{{illegal call to 'get_kernel_sub_group_count_for_ndrange', expected 'ndrange_t' argument type}}
  buf[0] = get_kernel_sub_group_count_for_ndrange(n, 1); // expected-error{{illegal call to 'get_kernel_sub_group_count_for_ndrange', expected block argument type}}
}

#pragma OPENCL EXTENSION cl_khr_subgroups : disable

kernel void foo1(global int *buf)
{
  ndrange_t n;
  buf[0] = get_kernel_max_sub_group_size_for_ndrange(n, ^(){}); // expected-error {{use of declaration 'get_kernel_max_sub_group_size_for_ndrange' requires cl_khr_subgroups extension to be enabled}}
}

kernel void bar1(global int *buf)
{
  ndrange_t n;
  buf[0] = get_kernel_sub_group_count_for_ndrange(n, ^(){}); // expected-error {{use of declaration 'get_kernel_sub_group_count_for_ndrange' requires cl_khr_subgroups extension to be enabled}}
}


Implement material roughness in tracer

Implement material roughness in tracer

Whenever a ray is reflected of a surgace, we check if the material includes a
non-zero roughness property. If it does we pertrub the ray in a cone
around the ideal reflection ray direction

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

#define DEBUG (get_global_id(0)==0 && get_global_id(1)==0)

// Render options
#define COS_WEIGHTED_LIGHT_SAMPLING
//#define CONE_LIGHT_SAMPLING

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

// Epsilon values
#define NUDGE_EPSILON 1.0e-3f
#define SNAP_EPSILON 1.0e-5f

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

// Supported material types
#define MATERIAL_DIFFUSE 0.0f
#define MATERIAL_SPECULAR 1.0f
#define MATERIAL_REFRACTIVE 2.0f
#define MATERIAL_EMISSIVE 3.0f

// Supported primitive types
#define PRIMITIVE_PLANE 0
#define PRIMITIVE_SPHERE 1
#define PRIMITIVE_BOX 2
#define PRIMITIVE_TRIANGE 3

// Packed data sampler
const sampler_t dataSampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;

// Data offset calculators. Buffers are stored as RGBA floats (16 bytes per pixel)
// - packed materials are 48 bytes long (3 pixels each)
// - packed primitives are 128 bytes long (8 pixels each)
#define MAT_OFFSET(itemIndex, field) (float)(itemIndex * 3 + field)
#define PRIM_OFFSET(itemIndex, field) (float)(itemIndex * 8 + field)

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

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

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

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

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

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

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

// Return random cosine weighted direction in a cone centered at the normal
// proportional to a solid angle. This function has:
// PDF = cos(theta) /  pi * sin(theta_max)^2
float4 rndCosWeightedConeDir(float4 normal, float sinThetaMax, uint2 *seed){
	float2 rnd  = clRand(seed);

	float rd = sinThetaMax * sqrt(rnd.x);
	float phi = 2.0f*M_PI*rnd.y;
	float r2t = sqrt(1 - rnd.x*sinThetaMax*sinThetaMax);

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

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

// Return random direction in a cone centered at the normal
// proportional to a solid angle. This function has:
// PDF = 1 / 2 * pi * (1 - cos(theta_max))
float4 rndConeDir(float4 normal, float cosThetaMax, uint2 *seed){
	float2 rnd  = clRand(seed);

	float rcd = 1.0f - rnd.x*(1.0f - cosThetaMax);
	float rd = sqrt(1.0f - rcd*rcd);
	float phi = 2.0f*M_PI*rnd.y;

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

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

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

typedef struct {
	float4 position;
	float4 normal;
	float dist;
	uint material;
	int primitive;
} Hit;

float intersectPlane(const float4 rayOrigin, const float4 rayDir, const float3 planeNormal, const float planeDist){
	float denom = dot(planeNormal, rayDir.xyz);
	if (fabs(denom) < 1e-6f) {
		return FLT_MAX;
	}

	float t = -(dot(planeNormal, rayOrigin.xyz) - planeDist) / denom;
	return t > 0.0f ? t : FLT_MAX;
}

float intersectSphere(const float4 rayOrigin, const float4 rayDir, const float4 sphereOrigin, const float sphereRadius){
	float4 op = sphereOrigin - rayOrigin;
	float b = dot(op, rayDir);
	float discr = b*b - dot(op, op) + sphereRadius * sphereRadius;
	if (discr<0.0f){
		return FLT_MAX;
	}
	discr = sqrt(discr);
	float t = b - discr;
	if(t > SNAP_EPSILON) {
		return t;
	}
	t = b + discr;
	return t > SNAP_EPSILON ? t : FLT_MAX;
}

float intersectBox(const float4 rayOrigin, const float4 rayDir, const float4 boxMin, const float4 boxMax){
	float4 tmin = (boxMin - rayOrigin) / rayDir;
	float4 tmax = (boxMax - rayOrigin) / rayDir;

	// If tmin > tmax we need to swap them
	float4 rmin = fmin(tmin, tmax);
	float4 rmax = fmax(tmin, tmax);

	float minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
	float maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
	return minmax >= maxmin && maxmin > SNAP_EPSILON ? maxmin : FLT_MAX;
}

float4 aabbNormal(const float4 hitPoint,  const float4 boxMin, const float4 boxMax){
	float4 rmin = fmin(boxMin, boxMax);
	float4 rmax = fmax(boxMin, boxMax);

	if (fabs(rmin.x - hitPoint.x) < SNAP_EPSILON){
		return (float4)(-1.0f, 0.0f, 0.0f, 0.0f);
	} else if (fabs(rmax.x - hitPoint.x) < SNAP_EPSILON){
		return (float4)(1.0f, 0.0f, 0.0f, 0.0f);
	} else if (fabs(rmin.y - hitPoint.y) < SNAP_EPSILON){
		return (float4)(0.0f, -1.0f, 0.0f,0.0f);
	} else if (fabs(rmax.y - hitPoint.y) < SNAP_EPSILON){
		return (float4)(0.0f, 1.0f, 0.0f,0.0f);
	} else if (fabs(rmin.z - hitPoint.z) < SNAP_EPSILON){
		return (float4)(0.0f, 0.0f, -1.0f,0.0f);
	}
	return (float4)(0.0f, 0.0f, 1.0f,0.0f);
}

float intersectTriangle(const float4 rayOrigin, const float4 rayDir, uint primIndex, image1d_t primitives, float4 *pointOut, float4 *normalOut, float2 *uvOut){
	// Calc intersection with triangle plane
	float4 normal = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2) );
	float d = intersectPlane(rayOrigin, rayDir, normal.xyz, normal.w);
	if( d == FLT_MAX ){
		return d;
	}

	// Check if hit point is inside the triangle by computing the dot product with edge planes
	// to get barycentric coordinates.
	float4 edge;
	float3 point = (rayOrigin + rayDir * d).xyz;

	edge = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,3) );
	float kt1 =  dot(point, edge.xyz) - edge.w;
	if( kt1 < 0.0f ){
		return FLT_MAX;
	}
	edge = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,4) );
	float kt2 =  dot(point, edge.xyz) - edge.w;
	if( kt2 < 0.0f ){
		return FLT_MAX;
	}
	edge = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,5) );
	float kt3 =  dot(point, edge.xyz) - edge.w;
	if( kt3 < 0.0f ){
		return FLT_MAX;
	}

	*pointOut = (float4)(point, 0);
	*normalOut = (float4)(normal.xyz, 0);
	return d;
}

// Check for the closest intersection with scene primitives. If no interection
// is detected, hit->dist should be set to FLT_MAX
void getIntersection(const float4 rayOrigin, const float4 rayDir, uint numPrimitives, image1d_t primitives, Hit *hit){
	hit->dist = FLT_MAX;
	hit->primitive = -1;

	float dist;
	float4 normal;
	float4 hitPoint;
	float4 origin, extents;
	float2 uv;
	for(int primIndex=0;primIndex<numPrimitives;primIndex++){
		// Get primitive type and material index
		// x: primitive type
		// y: material index
		float4 primInfo = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,0));

		// Check for ray-primitive intersection
		switch(uint(primInfo.x)) {
			case PRIMITIVE_PLANE:
				// Get primitive origin and dimensions
				origin = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,1));
				extents = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2));

				dist = intersectPlane(rayOrigin, rayDir, origin.xyz, extents.x);
				hitPoint = rayOrigin + rayDir * dist;
				normal = origin;
				break;
			case PRIMITIVE_SPHERE:
				// Get primitive origin and dimensions
				origin = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,1));
				extents = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2));

				dist = intersectSphere(rayOrigin, rayDir, origin, extents.x);
				hitPoint = rayOrigin + rayDir * dist;
				normal = normalize(hitPoint - origin);
				break;
			case PRIMITIVE_BOX:
				// Get primitive origin and dimensions
				origin = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,1));
				extents = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2));

				dist = intersectBox(rayOrigin, rayDir, origin, extents);
				hitPoint = rayOrigin + rayDir * dist;
				normal = aabbNormal(hitPoint, origin, extents);
				break;
			case PRIMITIVE_TRIANGE:
				dist = intersectTriangle(rayOrigin, rayDir, primIndex, primitives, &hitPoint, &normal, &uv);
				break;
		}

		if( dist < hit->dist ){
			hit->dist = dist;
			hit->position = hitPoint;
			hit->normal = normal;
			hit->material = uint(primInfo.y);
			hit->primitive = primIndex;
		}
	}
}

// Collect direct light at hit point.
float4 getDirectLight(Hit *hit, int numPrimitives, int numEmissiveIndices, image1d_t primitives, image1d_t materials, image1d_t emissiveIndices, uint2 *rndSeed){
	float4 light = 0.0f;
	float4 shadowRayOrigin = hit->position + hit->normal * NUDGE_EPSILON;
	Hit shadowHit;
	uint primIndex;
	for(int index=0;index<numEmissiveIndices;index++){

		// Fetch primitive index for emissive surface
		primIndex = read_imageui(emissiveIndices, dataSampler, index).x;

		// Get primitive type and material index
		// x: primitive type
		// y: material index
		float4 primInfo = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,0));

		// Get origin and extends. We will approximate the light with a sphere
		// positioned at the surface center having a radius equal to the greatest
		// distance to the primitive edges
		float4 origin = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,1));
		float4 extents = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2));
		float4 center;
		float radius;
		switch(uint(primInfo.x)){
			case PRIMITIVE_PLANE:
				// Plane lights not supported
				continue;
			case PRIMITIVE_SPHERE:
				center = origin;
				radius = extents.x;
				break;
			case PRIMITIVE_BOX:
				center = (origin + extents) * 0.5f;
				float4 size = fabs(origin - extents) * 0.5f;
				radius = VEC3_MAX_COMPONENT(size);
				break;
			case PRIMITIVE_TRIANGE:
				center = origin;
				radius = extents.w;
				break;
			default:
				continue;
		}

		float4 lightDir = center - hit->position;

		// Early rejection for back-facing surfaces
		if(dot(lightDir, hit->normal) < 0.0f){
			continue;
		}

		// Treat the light as a sphere with radius the largest dimension; then
		// generate a lightDir vector to its center and a tangent vector so as to
		// form a right-angled triangle with base (light radius, light dir) and
		// hypotenuse the tangent vector. Then generate a random ray towards
		// the light proportional to the solid angle of the cone defined by
		// the triangle.
		float lightDirLen = length(lightDir);
		float tLen = hypot(lightDirLen, radius);
		lightDir = normalize(lightDir);
#ifdef CONE_LIGHT_SAMPLING
		float cosThetaMax = lightDirLen / tLen;
		float4 shadowDir = rndConeDir(lightDir, cosThetaMax, rndSeed);
		float pdf = dot(hit->normal, shadowDir) * 2.0f * (1.0f - cosThetaMax);
#endif
#ifdef COS_WEIGHTED_LIGHT_SAMPLING
		float sinThetaMax = radius / tLen;
		float4 shadowDir = rndCosWeightedConeDir(lightDir, sinThetaMax, rndSeed);
		float pdf = sinThetaMax * sinThetaMax;
#endif

		// Check line of sight; skip if we hit anything other than the target light
		getIntersection(shadowRayOrigin, shadowDir, numPrimitives, primitives, &shadowHit);
		if(shadowHit.primitive != primIndex){
			continue;
		}

		// Radiance = 1/pi * cos(theta) * emissive * PDF
		float4 emissive = read_imagef(materials, dataSampler, MAT_OFFSET(primInfo.y, 2));
		light += emissive * pdf;
	}
	return light;
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir, int numPrimitives, int numEmissiveIndices, image1d_t primitives, image1d_t materials, image1d_t emissiveIndices, uint2 *rndSeed){
	Hit hit;
	float4 rCol = (float4)(0.0f);
	float4 mask = (float4)(1.0f);
	unsigned int bounce = 0;
	bool hitDiffuseObject = false;
	float nc, nt, eta, ddn;
	float4 nl, materialProps, diffuse;
	for(bounce=0;bounce<MAX_BOUNCES;bounce++){
		getIntersection(rayOrigin, rayDir, numPrimitives, primitives, &hit);

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

		// Get material properties
		materialProps = read_imagef(materials, dataSampler, MAT_OFFSET(hit.material, 0));

		// If we hit a light just add its emissive property and stop
		if( materialProps.x == MATERIAL_EMISSIVE ){
			if ( !hitDiffuseObject ){
				rCol += mask * read_imagef(materials, dataSampler, MAT_OFFSET(hit.material, 2));
			}
			break;
		}

		// Fetch diffuse color
		diffuse = read_imagef(materials, dataSampler, MAT_OFFSET(hit.material, 1));

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

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

		// Handle material surface type	(BRDF)
		if( materialProps.x == MATERIAL_DIFFUSE ) {
			// Mask outgoing reflectance by material diffuse property
			mask *= diffuse;
			hitDiffuseObject = true;
			rCol += mask * getDirectLight(&hit, numPrimitives, numEmissiveIndices, primitives, materials, emissiveIndices, rndSeed);

			// We do importance sampling for diffuse rays
			rayOrigin = hit.position + hit.normal * NUDGE_EPSILON;
			rayDir = rndCosWeightedHemisphereDir(hit.normal, rndSeed);
		} else if ( materialProps.x == MATERIAL_SPECULAR){
			// Mask outgoing reflectance by material diffuse property
			mask *= diffuse;

			// Reflect ray around normal
			rayOrigin = hit.position + hit.normal * NUDGE_EPSILON;
			rayDir = normalize(-2.0f * dot(hit.normal, rayDir) * hit.normal + rayDir);

			// If a non-zero roughness value is specified, take a cone-sample instead
			if( materialProps.z > 0.0f){
				rayDir = rndConeDir(rayDir, 1.0f - materialProps.z, rndSeed);
			}
		} else if (materialProps.x == MATERIAL_REFRACTIVE){
			// Mask outgoing reflectance by material diffuse property
			mask *= diffuse;

			// Generate correctly oriented normal
			nl = dot(rayDir, hit.normal) < 0 ? hit.normal : -1.0f * hit.normal;
			ddn = dot(rayDir, nl);

			// ray going into object
			bool into = dot(nl, hit.normal) > 0.0f;
			if (into){
				nc = 1.0f; // air
				nt = materialProps.y;
			} else {
				nc = materialProps.y;
				nt = 1.0f; // air
			}
			eta = nc / nt;
			float cos2t = 1.0f - eta * eta * (1.0f - ddn*ddn);

			// Calc reflection ray
			float4 reflRay = normalize(rayDir - 2.0f * hit.normal * dot(hit.normal, rayDir));

			// If a non-zero roughness value is specified, take a cone-sample instead
			if( materialProps.z > 0.0f){
				reflRay = rndConeDir(reflRay, 1.0f - materialProps.z, rndSeed);
			}

			// Total internal reflection
			if( cos2t < 0.0f){
				// Reflect ray around normal
				rayOrigin = hit.position + nl * NUDGE_EPSILON;
				rayDir = reflRay;
			} else {
				// Compute transmission ray dir
				float4 transDir = normalize(rayDir * eta - hit.normal * ((into ? 1.0f : -1.0f) * (ddn*eta + sqrt(cos2t))));

				// Compute reflectance at normal angle (R0) and reflectance (re) using
				// Schlick's approximation:
				float R0 = (nt-nc)*(nt-nc) / (nt+nc)*(nt+nc);
				// c = cos of transmission ray and normal
				float c= 1.0f - (into ? -ddn : dot(transDir,hit.normal));
				// This is Schlick's approximation for reflectance at angle theta
				float re = R0 + (1.0f - R0) * c * c * c * c * c;
				float tr = 1.0f - re;
				float prob = 0.25f + 0.5f * re;
				// Calc reflection and transmission boost factor based on event probablilities
				float rp = re / prob;
				float tp = tr / (1.0f - prob);

				// Use RR to pick reflection or transmission
				float2 rnd = clRand(rndSeed);
				if( rnd.x < 0.25f) {
					mask *= rp;

					// Reflect ray around normal
					rayOrigin = hit.position + nl * SNAP_EPSILON;
					rayDir = reflRay;
				} else {
					mask *= tp;
					rayOrigin = hit.position - nl * SNAP_EPSILON;
					rayDir = transDir;
				}
			}
		}
	}

	return rCol;
}

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

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

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

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

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

	// Average samples
	accum *= accumScaler;

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

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

#define DEBUG (get_global_id(0)==0 && get_global_id(1)==0)

// Render options
#define COS_WEIGHTED_LIGHT_SAMPLING
//#define CONE_LIGHT_SAMPLING

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

// Epsilon values
#define NUDGE_EPSILON 1.0e-3f
#define SNAP_EPSILON 1.0e-5f

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

// Supported material types
#define MATERIAL_DIFFUSE 0.0f
#define MATERIAL_SPECULAR 1.0f
#define MATERIAL_REFRACTIVE 2.0f
#define MATERIAL_EMISSIVE 3.0f

// Supported primitive types
#define PRIMITIVE_PLANE 0
#define PRIMITIVE_SPHERE 1
#define PRIMITIVE_BOX 2
#define PRIMITIVE_TRIANGE 3

// Packed data sampler
const sampler_t dataSampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;

// Data offset calculators. Buffers are stored as RGBA floats (16 bytes per pixel)
// - packed materials are 48 bytes long (3 pixels each)
// - packed primitives are 128 bytes long (8 pixels each)
#define MAT_OFFSET(itemIndex, field) (float)(itemIndex * 3 + field)
#define PRIM_OFFSET(itemIndex, field) (float)(itemIndex * 8 + field)

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

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

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

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

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

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

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

// Return random cosine weighted direction in a cone centered at the normal
// proportional to a solid angle. This function has:
// PDF = cos(theta) /  pi * sin(theta_max)^2
float4 rndCosWeightedConeDir(float4 normal, float sinThetaMax, uint2 *seed){
	float2 rnd  = clRand(seed);

	float rd = sinThetaMax * sqrt(rnd.x);
	float phi = 2.0f*M_PI*rnd.y;
	float r2t = sqrt(1 - rnd.x*sinThetaMax*sinThetaMax);

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

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

// Return random direction in a cone centered at the normal
// proportional to a solid angle. This function has:
// PDF = 1 / 2 * pi * (1 - cos(theta_max))
float4 rndConeDir(float4 normal, float cosThetaMax, uint2 *seed){
	float2 rnd  = clRand(seed);

	float rcd = 1.0f - rnd.x*(1.0f - cosThetaMax);
	float rd = sqrt(1.0f - rcd*rcd);
	float phi = 2.0f*M_PI*rnd.y;

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

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

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

typedef struct {
	float4 position;
	float4 normal;
	float dist;
	uint material;
	int primitive;
} Hit;

float intersectPlane(const float4 rayOrigin, const float4 rayDir, const float3 planeNormal, const float planeDist){
	float denom = dot(planeNormal, rayDir.xyz);
	if (fabs(denom) < 1e-6f) {
		return FLT_MAX;
	}

	float t = -(dot(planeNormal, rayOrigin.xyz) - planeDist) / denom;
	return t > 0.0f ? t : FLT_MAX;
}

float intersectSphere(const float4 rayOrigin, const float4 rayDir, const float4 sphereOrigin, const float sphereRadius){
	float4 op = sphereOrigin - rayOrigin;
	float b = dot(op, rayDir);
	float discr = b*b - dot(op, op) + sphereRadius * sphereRadius;
	if (discr<0.0f){
		return FLT_MAX;
	}
	discr = sqrt(discr);
	float t = b - discr;
	if(t > SNAP_EPSILON) {
		return t;
	}
	t = b + discr;
	return t > SNAP_EPSILON ? t : FLT_MAX;
}

float intersectBox(const float4 rayOrigin, const float4 rayDir, const float4 boxMin, const float4 boxMax){
	float4 tmin = (boxMin - rayOrigin) / rayDir;
	float4 tmax = (boxMax - rayOrigin) / rayDir;

	// If tmin > tmax we need to swap them
	float4 rmin = fmin(tmin, tmax);
	float4 rmax = fmax(tmin, tmax);

	float minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
	float maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
	return minmax >= maxmin && maxmin > SNAP_EPSILON ? maxmin : FLT_MAX;
}

float4 aabbNormal(const float4 hitPoint,  const float4 boxMin, const float4 boxMax){
	float4 rmin = fmin(boxMin, boxMax);
	float4 rmax = fmax(boxMin, boxMax);

	if (fabs(rmin.x - hitPoint.x) < SNAP_EPSILON){
		return (float4)(-1.0f, 0.0f, 0.0f, 0.0f);
	} else if (fabs(rmax.x - hitPoint.x) < SNAP_EPSILON){
		return (float4)(1.0f, 0.0f, 0.0f, 0.0f);
	} else if (fabs(rmin.y - hitPoint.y) < SNAP_EPSILON){
		return (float4)(0.0f, -1.0f, 0.0f,0.0f);
	} else if (fabs(rmax.y - hitPoint.y) < SNAP_EPSILON){
		return (float4)(0.0f, 1.0f, 0.0f,0.0f);
	} else if (fabs(rmin.z - hitPoint.z) < SNAP_EPSILON){
		return (float4)(0.0f, 0.0f, -1.0f,0.0f);
	}
	return (float4)(0.0f, 0.0f, 1.0f,0.0f);
}

float intersectTriangle(const float4 rayOrigin, const float4 rayDir, uint primIndex, image1d_t primitives, float4 *pointOut, float4 *normalOut, float2 *uvOut){
	// Calc intersection with triangle plane
	float4 normal = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2) );
	float d = intersectPlane(rayOrigin, rayDir, normal.xyz, normal.w);
	if( d == FLT_MAX ){
		return d;
	}

	// Check if hit point is inside the triangle by computing the dot product with edge planes
	// to get barycentric coordinates.
	float4 edge;
	float3 point = (rayOrigin + rayDir * d).xyz;

	edge = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,3) );
	float kt1 =  dot(point, edge.xyz) - edge.w;
	if( kt1 < 0.0f ){
		return FLT_MAX;
	}
	edge = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,4) );
	float kt2 =  dot(point, edge.xyz) - edge.w;
	if( kt2 < 0.0f ){
		return FLT_MAX;
	}
	edge = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,5) );
	float kt3 =  dot(point, edge.xyz) - edge.w;
	if( kt3 < 0.0f ){
		return FLT_MAX;
	}

	*pointOut = (float4)(point, 0);
	*normalOut = (float4)(normal.xyz, 0);
	return d;
}

// Check for the closest intersection with scene primitives. If no interection
// is detected, hit->dist should be set to FLT_MAX
void getIntersection(const float4 rayOrigin, const float4 rayDir, uint numPrimitives, image1d_t primitives, Hit *hit){
	hit->dist = FLT_MAX;
	hit->primitive = -1;

	float dist;
	float4 normal;
	float4 hitPoint;
	float4 origin, extents;
	float2 uv;
	for(int primIndex=0;primIndex<numPrimitives;primIndex++){
		// Get primitive type and material index
		// x: primitive type
		// y: material index
		float4 primInfo = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,0));

		// Check for ray-primitive intersection
		switch(uint(primInfo.x)) {
			case PRIMITIVE_PLANE:
				// Get primitive origin and dimensions
				origin = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,1));
				extents = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2));

				dist = intersectPlane(rayOrigin, rayDir, origin.xyz, extents.x);
				hitPoint = rayOrigin + rayDir * dist;
				normal = origin;
				break;
			case PRIMITIVE_SPHERE:
				// Get primitive origin and dimensions
				origin = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,1));
				extents = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2));

				dist = intersectSphere(rayOrigin, rayDir, origin, extents.x);
				hitPoint = rayOrigin + rayDir * dist;
				normal = normalize(hitPoint - origin);
				break;
			case PRIMITIVE_BOX:
				// Get primitive origin and dimensions
				origin = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,1));
				extents = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2));

				dist = intersectBox(rayOrigin, rayDir, origin, extents);
				hitPoint = rayOrigin + rayDir * dist;
				normal = aabbNormal(hitPoint, origin, extents);
				break;
			case PRIMITIVE_TRIANGE:
				dist = intersectTriangle(rayOrigin, rayDir, primIndex, primitives, &hitPoint, &normal, &uv);
				break;
		}

		if( dist < hit->dist ){
			hit->dist = dist;
			hit->position = hitPoint;
			hit->normal = normal;
			hit->material = uint(primInfo.y);
			hit->primitive = primIndex;
		}
	}
}

// Collect direct light at hit point.
float4 getDirectLight(Hit *hit, int numPrimitives, int numEmissiveIndices, image1d_t primitives, image1d_t materials, image1d_t emissiveIndices, uint2 *rndSeed){
	float4 light = 0.0f;
	float4 shadowRayOrigin = hit->position + hit->normal * NUDGE_EPSILON;
	Hit shadowHit;
	uint primIndex;
	for(int index=0;index<numEmissiveIndices;index++){

		// Fetch primitive index for emissive surface
		primIndex = read_imageui(emissiveIndices, dataSampler, index).x;

		// Get primitive type and material index
		// x: primitive type
		// y: material index
		float4 primInfo = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,0));

		// Get origin and extends. We will approximate the light with a sphere
		// positioned at the surface center having a radius equal to the greatest
		// distance to the primitive edges
		float4 origin = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,1));
		float4 extents = read_imagef(primitives, dataSampler, PRIM_OFFSET(primIndex,2));
		float4 center;
		float radius;
		switch(uint(primInfo.x)){
			case PRIMITIVE_PLANE:
				// Plane lights not supported
				continue;
			case PRIMITIVE_SPHERE:
				center = origin;
				radius = extents.x;
				break;
			case PRIMITIVE_BOX:
				center = (origin + extents) * 0.5f;
				float4 size = fabs(origin - extents) * 0.5f;
				radius = VEC3_MAX_COMPONENT(size);
				break;
			case PRIMITIVE_TRIANGE:
				center = origin;
				radius = extents.w;
				break;
			default:
				continue;
		}

		float4 lightDir = center - hit->position;

		// Early rejection for back-facing surfaces
		if(dot(lightDir, hit->normal) < 0.0f){
			continue;
		}

		// Treat the light as a sphere with radius the largest dimension; then
		// generate a lightDir vector to its center and a tangent vector so as to
		// form a right-angled triangle with base (light radius, light dir) and
		// hypotenuse the tangent vector. Then generate a random ray towards
		// the light proportional to the solid angle of the cone defined by
		// the triangle.
		float lightDirLen = length(lightDir);
		float tLen = hypot(lightDirLen, radius);
		lightDir = normalize(lightDir);
#ifdef CONE_LIGHT_SAMPLING
		float cosThetaMax = lightDirLen / tLen;
		float4 shadowDir = rndConeDir(lightDir, cosThetaMax, rndSeed);
		float pdf = dot(hit->normal, shadowDir) * 2.0f * (1.0f - cosThetaMax);
#endif
#ifdef COS_WEIGHTED_LIGHT_SAMPLING
		float sinThetaMax = radius / tLen;
		float4 shadowDir = rndCosWeightedConeDir(lightDir, sinThetaMax, rndSeed);
		float pdf = sinThetaMax * sinThetaMax;
#endif

		// Check line of sight; skip if we hit anything other than the target light
		getIntersection(shadowRayOrigin, shadowDir, numPrimitives, primitives, &shadowHit);
		if(shadowHit.primitive != primIndex){
			continue;
		}

		// Radiance = 1/pi * cos(theta) * emissive * PDF
		float4 emissive = read_imagef(materials, dataSampler, MAT_OFFSET(primInfo.y, 2));
		light += emissive * pdf;
	}
	return light;
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir, int numPrimitives, int numEmissiveIndices, image1d_t primitives, image1d_t materials, image1d_t emissiveIndices, uint2 *rndSeed){
	Hit hit;
	float4 rCol = (float4)(0.0f);
	float4 mask = (float4)(1.0f);
	unsigned int bounce = 0;
	bool hitDiffuseObject = false;
	float nc, nt, eta, ddn;
	float4 nl, materialProps, diffuse;
	for(bounce=0;bounce<MAX_BOUNCES;bounce++){
		getIntersection(rayOrigin, rayDir, numPrimitives, primitives, &hit);

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

		// Get material properties
		materialProps = read_imagef(materials, dataSampler, MAT_OFFSET(hit.material, 0));

		// If we hit a light just add its emissive property and stop
		if( materialProps.x == MATERIAL_EMISSIVE ){
			if ( !hitDiffuseObject ){
				rCol += mask * read_imagef(materials, dataSampler, MAT_OFFSET(hit.material, 2));
			}
			break;
		}

		// Fetch diffuse color
		diffuse = read_imagef(materials, dataSampler, MAT_OFFSET(hit.material, 1));

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

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

		// Handle material surface type	(BRDF)
		if( materialProps.x == MATERIAL_DIFFUSE ) {
			// Mask outgoing reflectance by material diffuse property
			mask *= diffuse;
			hitDiffuseObject = true;
			rCol += mask * getDirectLight(&hit, numPrimitives, numEmissiveIndices, primitives, materials, emissiveIndices, rndSeed);

			// We do importance sampling for diffuse rays
			rayOrigin = hit.position + hit.normal * NUDGE_EPSILON;
			rayDir = rndCosWeightedHemisphereDir(hit.normal, rndSeed);
		} else if ( materialProps.x == MATERIAL_SPECULAR){
			// Mask outgoing reflectance by material diffuse property
			mask *= diffuse;

			// Reflect ray around normal
			rayOrigin = hit.position + hit.normal * NUDGE_EPSILON;
			rayDir = normalize(-2.0f * dot(hit.normal, rayDir) * hit.normal + rayDir);
		} else if (materialProps.x == MATERIAL_REFRACTIVE){
			// Mask outgoing reflectance by material diffuse property
			mask *= diffuse;

			// Generate correctly oriented normal
			nl = dot(rayDir, hit.normal) < 0 ? hit.normal : -1.0f * hit.normal;
			ddn = dot(rayDir, nl);

			// ray going into object
			bool into = dot(nl, hit.normal) > 0.0f;
			if (into){
				nc = 1.0f; // air
				nt = materialProps.y;
			} else {
				nc = materialProps.y;
				nt = 1.0f; // air
			}
			eta = nc / nt;
			float cos2t = 1.0f - eta * eta * (1.0f - ddn*ddn);

			// Total internal reflection
			if( cos2t < 0.0f){
				// Reflect ray around normal
				rayOrigin = hit.position + nl * NUDGE_EPSILON;
				rayDir = normalize(rayDir - 2.0f * hit.normal * dot(hit.normal, rayDir));
			} else {
				// Compute transmission ray dir
				float4 transDir = normalize(rayDir * eta - hit.normal * ((into ? 1.0f : -1.0f) * (ddn*eta + sqrt(cos2t))));

				// Compute reflectance at normal angle (R0) and reflectance (re) using
				// Schlick's approximation:
				float R0 = (nt-nc)*(nt-nc) / (nt+nc)*(nt+nc);
				// c = cos of transmission ray and normal
				float c= 1.0f - (into ? -ddn : dot(transDir,hit.normal));
				// This is Schlick's approximation for reflectance at angle theta
				float re = R0 + (1.0f - R0) * c * c * c * c * c;
				float tr = 1.0f - re;
				float prob = 0.25f + 0.5f * re;
				// Calc reflection and transmission boost factor based on event probablilities
				float rp = re / prob;
				float tp = tr / (1.0f - prob);

				// Use RR to pick reflection or transmission
				float2 rnd = clRand(rndSeed);
				if( rnd.x < 0.25f) {
					mask *= rp;

					// Reflect ray around normal
					rayOrigin = hit.position + nl * SNAP_EPSILON;
					rayDir = normalize(rayDir - 2.0f * hit.normal * dot(hit.normal, rayDir));
				} else {
					mask *= tp;
					rayOrigin = hit.position - nl * SNAP_EPSILON;
					rayDir = transDir;
				}
			}
		}
	}

	return rCol;
}

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

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

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

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

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

	// Average samples
	accum *= accumScaler;

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


R600: Map address spaces for atomic_min

R600: Map address spaces for atomic_min

Signed-off-by: Aaron Watry <awatry@gmail.com>
Reviewed-by: Tom Stellard <thomas.stellard@amd.com>

git-svn-id: e7f1ab7c2a259259587475a3e7520e757882f167@217923 91177308-0d34-0410-b5e6-96231b3b80d8

#include <clc/clc.h>

#define ATOMIC_FUNC_DEFINE(RET_SIGN, ARG_SIGN, TYPE, CL_FUNCTION, CLC_FUNCTION, CL_ADDRSPACE, LLVM_ADDRSPACE) \
_CLC_OVERLOAD _CLC_DEF RET_SIGN TYPE CL_FUNCTION (volatile CL_ADDRSPACE RET_SIGN TYPE *p, RET_SIGN TYPE val) { \
	return (RET_SIGN TYPE)__clc_##CLC_FUNCTION##_addr##LLVM_ADDRSPACE((volatile CL_ADDRSPACE ARG_SIGN TYPE*)p, (ARG_SIGN TYPE)val); \
}

/* For atomic functions that don't need different bitcode dependending on argument signedness */
#define ATOMIC_FUNC_SIGN(TYPE, FUNCTION, CL_ADDRSPACE, LLVM_ADDRSPACE) \
	_CLC_DECL signed TYPE __clc_##FUNCTION##_addr##LLVM_ADDRSPACE(volatile CL_ADDRSPACE signed TYPE*, signed TYPE); \
	ATOMIC_FUNC_DEFINE(signed, signed, TYPE, FUNCTION, FUNCTION, CL_ADDRSPACE, LLVM_ADDRSPACE) \
	ATOMIC_FUNC_DEFINE(unsigned, signed, TYPE, FUNCTION, FUNCTION, CL_ADDRSPACE, LLVM_ADDRSPACE)

#define ATOMIC_FUNC_ADDRSPACE(TYPE, FUNCTION) \
	ATOMIC_FUNC_SIGN(TYPE, FUNCTION, global, 1) \
	ATOMIC_FUNC_SIGN(TYPE, FUNCTION, local, 3)

#define ATOMIC_FUNC(FUNCTION) \
	ATOMIC_FUNC_ADDRSPACE(int, FUNCTION)

ATOMIC_FUNC(atomic_add)
ATOMIC_FUNC(atomic_and)
ATOMIC_FUNC(atomic_or)
ATOMIC_FUNC(atomic_sub)
ATOMIC_FUNC(atomic_xor)

_CLC_DECL signed int __clc_atomic_max_addr1(volatile global signed int*, signed int);
_CLC_DECL signed int __clc_atomic_max_addr3(volatile local signed int*, signed int);
_CLC_DECL uint __clc_atomic_umax_addr1(volatile global uint*, uint);
_CLC_DECL uint __clc_atomic_umax_addr3(volatile local uint*, uint);

ATOMIC_FUNC_DEFINE(signed, signed, int, atomic_max, atomic_max, global, 1)
ATOMIC_FUNC_DEFINE(signed, signed, int, atomic_max, atomic_max, local, 3)
ATOMIC_FUNC_DEFINE(unsigned, unsigned, int, atomic_max, atomic_umax, global, 1)
ATOMIC_FUNC_DEFINE(unsigned, unsigned, int, atomic_max, atomic_umax, local, 3)

_CLC_DECL signed int __clc_atomic_min_addr1(volatile global signed int*, signed int);
_CLC_DECL signed int __clc_atomic_min_addr3(volatile local signed int*, signed int);
_CLC_DECL uint __clc_atomic_umin_addr1(volatile global uint*, uint);
_CLC_DECL uint __clc_atomic_umin_addr3(volatile local uint*, uint);

ATOMIC_FUNC_DEFINE(signed, signed, int, atomic_min, atomic_min, global, 1)
ATOMIC_FUNC_DEFINE(signed, signed, int, atomic_min, atomic_min, local, 3)
ATOMIC_FUNC_DEFINE(unsigned, unsigned, int, atomic_min, atomic_umin, global, 1)
ATOMIC_FUNC_DEFINE(unsigned, unsigned, int, atomic_min, atomic_umin, local, 3)


Removed address space qualifiers from OpenCL kernel functions

These aren't needed and will raise errors on some implementations.

//
//   Copyright 2013 Pixar
//
//   Licensed under the Apache License, Version 2.0 (the "Apache License")
//   with the following modification; you may not use this file except in
//   compliance with the Apache License and the following modification to it:
//   Section 6. Trademarks. is deleted and replaced with:
//
//   6. Trademarks. This License does not grant permission to use the trade
//      names, trademarks, service marks, or product names of the Licensor
//      and its affiliates, except as required to comply with Section 4(c) of
//      the License and to reproduce the content of the NOTICE file.
//
//   You may obtain a copy of the Apache License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the Apache License with the above modification is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//   KIND, either express or implied. See the Apache License for the specific
//   language governing permissions and limitations under the Apache License.
//

#ifndef M_PI
    #define M_PI 3.14159265358979323846  // fix for OSX 10.8 (M_PI is in the Khronos standard...)
#endif

struct Vertex
{
    float v[NUM_VERTEX_ELEMENTS];
};

struct Varying
{
    float v[NUM_VARYING_ELEMENTS];
};

void clearVertex(struct Vertex *vertex) {

    for (int i = 0; i < NUM_VERTEX_ELEMENTS; i++) {
        vertex->v[i] = 0;
    }
}
void clearVarying(struct Varying *varying) {

    for (int i = 0; i < NUM_VARYING_ELEMENTS; i++) {
        varying->v[i] = 0;
    }
}

void addWithWeight(struct Vertex *dst, __global struct Vertex *src, float weight) {

    for (int i = 0; i < NUM_VERTEX_ELEMENTS; i++) {
        dst->v[i] += src->v[i] * weight;
    }
}

void addVaryingWithWeight(struct Varying *dst, __global struct Varying *src, float weight) {

    for (int i = 0; i < NUM_VARYING_ELEMENTS; i++) {
        dst->v[i] += src->v[i] * weight;
    }
}

__kernel void computeBilinearEdge(__global struct Vertex *vertex,
                                  __global struct Varying *varying,
                                  __global int *E_IT,
                                  int vertexOffset, int tableOffset,
                                  int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int eidx0 = E_IT[2*i+0];
    int eidx1 = E_IT[2*i+1];

    struct Vertex dst;
    struct Varying dstVarying;
    clearVertex(&dst);
    clearVarying(&dstVarying);

    addWithWeight(&dst, &vertex[eidx0], 0.5f);
    addWithWeight(&dst, &vertex[eidx1], 0.5f);

    vertex[vid] = dst;

    if (varying) {
        addVaryingWithWeight(&dstVarying, &varying[eidx0], 0.5f);
        addVaryingWithWeight(&dstVarying, &varying[eidx1], 0.5f);
        varying[vid] = dstVarying;
    }
}

__kernel void computeBilinearVertex(__global struct Vertex *vertex,
                                    __global struct Varying *varying,
                                    __global int *V_ITa,
                                    int vertexOffset, int tableOffset,
                                    int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;

    int p = V_ITa[i];

    struct Vertex dst;
    clearVertex(&dst);
    addWithWeight(&dst, &vertex[p], 1.0f);

    vertex[vid] = dst;

    if (varying) {
        struct Varying dstVarying;
        clearVarying(&dstVarying);
        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
        varying[vid] = dstVarying;
    }
}

// ----------------------------------------------------------------------------------------

__kernel void computeFace(__global struct Vertex *vertex,
                          __global struct Varying *varying,
                          __global int *F_IT,
                          __global int *F_ITa,
                          int vertexOffset, int tableOffset,
                          int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int h = F_ITa[2*i];
    int n = F_ITa[2*i+1];

    float weight = 1.0f/n;

    struct Vertex dst;
    struct Varying dstVarying;
    clearVertex(&dst);
    clearVarying(&dstVarying);
    for (int j=0; j<n; ++j) {
        int index = F_IT[h+j];
        addWithWeight(&dst, &vertex[index], weight);
        if(varying) addVaryingWithWeight(&dstVarying, &varying[index], weight);
    }
    vertex[vid] = dst;
    if (varying) varying[vid] = dstVarying;
}

__kernel void computeEdge(__global struct Vertex *vertex,
                          __global struct Varying *varying,
                          __global int *E_IT,
                          __global float *E_W,
                          int vertexOffset, int tableOffset,
                          int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int eidx0 = E_IT[4*i+0];
    int eidx1 = E_IT[4*i+1];
    int eidx2 = E_IT[4*i+2];
    int eidx3 = E_IT[4*i+3];

    float vertWeight = E_W[i*2+0];

    // Fully sharp edge : vertWeight = 0.5f;
    struct Vertex dst;
    struct Varying dstVarying;
    clearVertex(&dst);
    clearVarying(&dstVarying);

    addWithWeight(&dst, &vertex[eidx0], vertWeight);
    addWithWeight(&dst, &vertex[eidx1], vertWeight);

    if (eidx2 > -1) {
        float faceWeight = E_W[i*2+1];

        addWithWeight(&dst, &vertex[eidx2], faceWeight);
        addWithWeight(&dst, &vertex[eidx3], faceWeight);
    }

    vertex[vid] = dst;

    if (varying) {
        addVaryingWithWeight(&dstVarying, &varying[eidx0], 0.5f);
        addVaryingWithWeight(&dstVarying, &varying[eidx1], 0.5f);
        varying[vid] = dstVarying;
    }
}

__kernel void computeVertexA(__global struct Vertex *vertex,
                             __global struct Varying *varying,
                             __global int *V_ITa,
                             __global float *V_W,
                             int vertexOffset, int tableOffset,
                             int start, int end, int pass) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int n     = V_ITa[5*i+1];
    int p     = V_ITa[5*i+2];
    int eidx0 = V_ITa[5*i+3];
    int eidx1 = V_ITa[5*i+4];

    float weight = (pass==1) ? V_W[i] : 1.0f - V_W[i];

    // In the case of fractional weight, the weight must be inverted since
    // the value is shared with the k_Smooth kernel (statistically the
    // k_Smooth kernel runs much more often than this one)
    if (weight>0.0f && weight<1.0f && n > 0)
        weight=1.0f-weight;

    struct Vertex dst;
    if (! pass)
        clearVertex(&dst);
    else
        dst = vertex[vid];

    if (eidx0==-1 || (pass==0 && (n==-1)) ) {
        addWithWeight(&dst, &vertex[p], weight);
    } else {
        addWithWeight(&dst, &vertex[p], weight * 0.75f);
        addWithWeight(&dst, &vertex[eidx0], weight * 0.125f);
        addWithWeight(&dst, &vertex[eidx1], weight * 0.125f);
    }
    vertex[vid] = dst;

    if (! pass && varying) {
        struct Varying dstVarying;
        clearVarying(&dstVarying);
        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
        varying[vid] = dstVarying;
    }
}

__kernel void computeVertexB(__global struct Vertex *vertex,
                             __global struct Varying *varying,
                             __global int *V_ITa,
                             __global int *V_IT,
                             __global float *V_W,
                             int vertexOffset, int tableOffset,
                             int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int h = V_ITa[5*i];
    int n = V_ITa[5*i+1];
    int p = V_ITa[5*i+2];

    float weight = V_W[i];
    float wp = 1.0f/(float)(n*n);
    float wv = (n-2.0f) * n * wp;

    struct Vertex dst;
    clearVertex(&dst);

    addWithWeight(&dst, &vertex[p], weight * wv);

    for (int j = 0; j < n; ++j) {
        addWithWeight(&dst, &vertex[V_IT[h+j*2]], weight * wp);
        addWithWeight(&dst, &vertex[V_IT[h+j*2+1]], weight * wp);
    }
    vertex[vid] = dst;

    if (varying) {
        struct Varying dstVarying;
        clearVarying(&dstVarying);
        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
        varying[vid] = dstVarying;
    }
}

__kernel void computeLoopVertexB(__global struct Vertex *vertex,
                                 __global struct Varying *varying,
                                 __global int *V_ITa,
                                 __global int *V_IT,
                                 __global float *V_W,
                                 int vertexOffset, int tableOffset,
                                 int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int h = V_ITa[5*i];
    int n = V_ITa[5*i+1];
    int p = V_ITa[5*i+2];

    float weight = V_W[i];
    float wp = 1.0f/(float)(n);
    float beta = 0.25f * cos((float)(M_PI) * 2.0f * wp) + 0.375f;
    beta = beta * beta;
    beta = (0.625f - beta) * wp;

    struct Vertex dst;
    clearVertex(&dst);
    addWithWeight(&dst, &vertex[p], weight * (1.0f - (beta * n)));

    for (int j = 0; j < n; ++j) {
        addWithWeight(&dst, &vertex[V_IT[h+j]], weight * beta);
    }
    vertex[vid] = dst;

    if (varying) {
        struct Varying dstVarying;
        clearVarying(&dstVarying);
        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
        varying[vid] = dstVarying;
    }
}

__kernel void editVertexAdd(__global struct Vertex *vertex,
                            __global int *editIndices,
                            __global float *editValues,
                            int primVarOffset,
                            int primVarWidth,
                            int vertexOffset, int tableOffset,
                            int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int v = editIndices[i];
    int eid = start + get_global_id(0);
    struct Vertex dst = vertex[v];

    for (int j = 0; j < primVarWidth; ++j) {
        dst.v[j+primVarOffset] += editValues[eid*primVarWidth + j];
    }
    vertex[v] = dst;
}

//
//   Copyright 2013 Pixar
//
//   Licensed under the Apache License, Version 2.0 (the "Apache License")
//   with the following modification; you may not use this file except in
//   compliance with the Apache License and the following modification to it:
//   Section 6. Trademarks. is deleted and replaced with:
//
//   6. Trademarks. This License does not grant permission to use the trade
//      names, trademarks, service marks, or product names of the Licensor
//      and its affiliates, except as required to comply with Section 4(c) of
//      the License and to reproduce the content of the NOTICE file.
//
//   You may obtain a copy of the Apache License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the Apache License with the above modification is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//   KIND, either express or implied. See the Apache License for the specific
//   language governing permissions and limitations under the Apache License.
//

#ifndef M_PI
    #define M_PI 3.14159265358979323846  // fix for OSX 10.8 (M_PI is in the Khronos standard...)
#endif

struct Vertex
{
    float v[NUM_VERTEX_ELEMENTS];
};

struct Varying
{
    float v[NUM_VARYING_ELEMENTS];
};

__global void clearVertex(struct Vertex *vertex) {

    for (int i = 0; i < NUM_VERTEX_ELEMENTS; i++) {
        vertex->v[i] = 0;
    }
}
__global void clearVarying(struct Varying *varying) {

    for (int i = 0; i < NUM_VARYING_ELEMENTS; i++) {
        varying->v[i] = 0;
    }
}

__global void addWithWeight(struct Vertex *dst, __global struct Vertex *src, float weight) {

    for (int i = 0; i < NUM_VERTEX_ELEMENTS; i++) {
        dst->v[i] += src->v[i] * weight;
    }
}

__global void addVaryingWithWeight(struct Varying *dst, __global struct Varying *src, float weight) {

    for (int i = 0; i < NUM_VARYING_ELEMENTS; i++) {
        dst->v[i] += src->v[i] * weight;
    }
}

__kernel void computeBilinearEdge(__global struct Vertex *vertex,
                                  __global struct Varying *varying,
                                  __global int *E_IT,
                                  int vertexOffset, int tableOffset,
                                  int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int eidx0 = E_IT[2*i+0];
    int eidx1 = E_IT[2*i+1];

    struct Vertex dst;
    struct Varying dstVarying;
    clearVertex(&dst);
    clearVarying(&dstVarying);

    addWithWeight(&dst, &vertex[eidx0], 0.5f);
    addWithWeight(&dst, &vertex[eidx1], 0.5f);

    vertex[vid] = dst;

    if (varying) {
        addVaryingWithWeight(&dstVarying, &varying[eidx0], 0.5f);
        addVaryingWithWeight(&dstVarying, &varying[eidx1], 0.5f);
        varying[vid] = dstVarying;
    }
}

__kernel void computeBilinearVertex(__global struct Vertex *vertex,
                                    __global struct Varying *varying,
                                    __global int *V_ITa,
                                    int vertexOffset, int tableOffset,
                                    int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;

    int p = V_ITa[i];

    struct Vertex dst;
    clearVertex(&dst);
    addWithWeight(&dst, &vertex[p], 1.0f);

    vertex[vid] = dst;

    if (varying) {
        struct Varying dstVarying;
        clearVarying(&dstVarying);
        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
        varying[vid] = dstVarying;
    }
}

// ----------------------------------------------------------------------------------------

__kernel void computeFace(__global struct Vertex *vertex,
                          __global struct Varying *varying,
                          __global int *F_IT,
                          __global int *F_ITa,
                          int vertexOffset, int tableOffset,
                          int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int h = F_ITa[2*i];
    int n = F_ITa[2*i+1];

    float weight = 1.0f/n;

    struct Vertex dst;
    struct Varying dstVarying;
    clearVertex(&dst);
    clearVarying(&dstVarying);
    for (int j=0; j<n; ++j) {
        int index = F_IT[h+j];
        addWithWeight(&dst, &vertex[index], weight);
        if(varying) addVaryingWithWeight(&dstVarying, &varying[index], weight);
    }
    vertex[vid] = dst;
    if (varying) varying[vid] = dstVarying;
}

__kernel void computeEdge(__global struct Vertex *vertex,
                          __global struct Varying *varying,
                          __global int *E_IT,
                          __global float *E_W,
                          int vertexOffset, int tableOffset,
                          int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int eidx0 = E_IT[4*i+0];
    int eidx1 = E_IT[4*i+1];
    int eidx2 = E_IT[4*i+2];
    int eidx3 = E_IT[4*i+3];

    float vertWeight = E_W[i*2+0];

    // Fully sharp edge : vertWeight = 0.5f;
    struct Vertex dst;
    struct Varying dstVarying;
    clearVertex(&dst);
    clearVarying(&dstVarying);

    addWithWeight(&dst, &vertex[eidx0], vertWeight);
    addWithWeight(&dst, &vertex[eidx1], vertWeight);

    if (eidx2 > -1) {
        float faceWeight = E_W[i*2+1];

        addWithWeight(&dst, &vertex[eidx2], faceWeight);
        addWithWeight(&dst, &vertex[eidx3], faceWeight);
    }

    vertex[vid] = dst;

    if (varying) {
        addVaryingWithWeight(&dstVarying, &varying[eidx0], 0.5f);
        addVaryingWithWeight(&dstVarying, &varying[eidx1], 0.5f);
        varying[vid] = dstVarying;
    }
}

__kernel void computeVertexA(__global struct Vertex *vertex,
                             __global struct Varying *varying,
                             __global int *V_ITa,
                             __global float *V_W,
                             int vertexOffset, int tableOffset,
                             int start, int end, int pass) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int n     = V_ITa[5*i+1];
    int p     = V_ITa[5*i+2];
    int eidx0 = V_ITa[5*i+3];
    int eidx1 = V_ITa[5*i+4];

    float weight = (pass==1) ? V_W[i] : 1.0f - V_W[i];

    // In the case of fractional weight, the weight must be inverted since
    // the value is shared with the k_Smooth kernel (statistically the
    // k_Smooth kernel runs much more often than this one)
    if (weight>0.0f && weight<1.0f && n > 0)
        weight=1.0f-weight;

    struct Vertex dst;
    if (! pass)
        clearVertex(&dst);
    else
        dst = vertex[vid];

    if (eidx0==-1 || (pass==0 && (n==-1)) ) {
        addWithWeight(&dst, &vertex[p], weight);
    } else {
        addWithWeight(&dst, &vertex[p], weight * 0.75f);
        addWithWeight(&dst, &vertex[eidx0], weight * 0.125f);
        addWithWeight(&dst, &vertex[eidx1], weight * 0.125f);
    }
    vertex[vid] = dst;

    if (! pass && varying) {
        struct Varying dstVarying;
        clearVarying(&dstVarying);
        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
        varying[vid] = dstVarying;
    }
}

__kernel void computeVertexB(__global struct Vertex *vertex,
                             __global struct Varying *varying,
                             __global int *V_ITa,
                             __global int *V_IT,
                             __global float *V_W,
                             int vertexOffset, int tableOffset,
                             int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int h = V_ITa[5*i];
    int n = V_ITa[5*i+1];
    int p = V_ITa[5*i+2];

    float weight = V_W[i];
    float wp = 1.0f/(float)(n*n);
    float wv = (n-2.0f) * n * wp;

    struct Vertex dst;
    clearVertex(&dst);

    addWithWeight(&dst, &vertex[p], weight * wv);

    for (int j = 0; j < n; ++j) {
        addWithWeight(&dst, &vertex[V_IT[h+j*2]], weight * wp);
        addWithWeight(&dst, &vertex[V_IT[h+j*2+1]], weight * wp);
    }
    vertex[vid] = dst;

    if (varying) {
        struct Varying dstVarying;
        clearVarying(&dstVarying);
        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
        varying[vid] = dstVarying;
    }
}

__kernel void computeLoopVertexB(__global struct Vertex *vertex,
                                 __global struct Varying *varying,
                                 __global int *V_ITa,
                                 __global int *V_IT,
                                 __global float *V_W,
                                 int vertexOffset, int tableOffset,
                                 int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int vid = start + get_global_id(0) + vertexOffset;
    int h = V_ITa[5*i];
    int n = V_ITa[5*i+1];
    int p = V_ITa[5*i+2];

    float weight = V_W[i];
    float wp = 1.0f/(float)(n);
    float beta = 0.25f * cos((float)(M_PI) * 2.0f * wp) + 0.375f;
    beta = beta * beta;
    beta = (0.625f - beta) * wp;

    struct Vertex dst;
    clearVertex(&dst);
    addWithWeight(&dst, &vertex[p], weight * (1.0f - (beta * n)));

    for (int j = 0; j < n; ++j) {
        addWithWeight(&dst, &vertex[V_IT[h+j]], weight * beta);
    }
    vertex[vid] = dst;

    if (varying) {
        struct Varying dstVarying;
        clearVarying(&dstVarying);
        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
        varying[vid] = dstVarying;
    }
}

__kernel void editVertexAdd(__global struct Vertex *vertex,
                            __global int *editIndices,
                            __global float *editValues,
                            int primVarOffset,
                            int primVarWidth,
                            int vertexOffset, int tableOffset,
                            int start, int end) {

    int i = start + get_global_id(0) + tableOffset;
    int v = editIndices[i];
    int eid = start + get_global_id(0);
    struct Vertex dst = vertex[v];

    for (int j = 0; j < primVarWidth; ++j) {
        dst.v[j+primVarOffset] += editValues[eid*primVarWidth + j];
    }
    vertex[v] = dst;
}