From 19ec835c693c5b0cc3bac63a7be2febdab149c8a Mon Sep 17 00:00:00 2001
From: Gary Frost <frost.gary@gmail.com>
Date: Mon, 13 Feb 2012 20:53:05 +0000
Subject: [PATCH] Merge of MultiDimSupport and SupportGlobalLocalMemory
 branches.

---
 build.xml                                     |    1 +
 com.amd.aparapi.jni/src/cpp/aparapi.cpp       | 2253 +++++++++--------
 com.amd.aparapi/build.xml                     |    2 +-
 .../src/java/com/amd/aparapi/ClassModel.java  |    4 +-
 .../src/java/com/amd/aparapi/Config.java      |    1 +
 .../com/amd/aparapi/DeprecatedException.java  |   46 +
 .../src/java/com/amd/aparapi/Kernel.java      |  306 ++-
 .../java/com/amd/aparapi/KernelRunner.java    |  529 ++--
 .../java/com/amd/aparapi/KernelWriter.java    |   74 +-
 .../src/java/com/amd/aparapi/MethodModel.java |    2 -
 .../src/java/com/amd/aparapi/Range.java       |  392 +++
 .../java/com/amd/aparapi/RangeException.java  |   46 +
 .../amd/aparapi/examples/effects/Main.java    |    7 +-
 examples/nbody/local.bat                      |   14 +
 .../com/amd/aparapi/examples/nbody/Local.java |  356 +++
 .../com/amd/aparapi/examples/nbody/Main.java  |   66 +-
 samples/blackscholes/.classpath               |    8 +
 samples/blackscholes/.project                 |   17 +
 .../aparapi/samples/blackscholes/Main.java    |   10 +-
 samples/convolution/.classpath                |    8 +
 samples/convolution/.project                  |   17 +
 samples/convolution/build.xml                 |   20 +
 samples/convolution/conv.bat                  |    6 +
 .../amd/aparapi/sample/convolution/Main.java  |  234 ++
 .../sample/convolution/Test12x4_4x2.java      |  496 ++++
 samples/convolution/testcard.jpg              |  Bin 0 -> 64477 bytes
 .../src/com/amd/aparapi/sample/life/Main.java |    7 +-
 samples/mandel/mandel2D.bat                   |    7 +
 samples/mandel/mandel2D.sh                    |    5 +
 .../com/amd/aparapi/sample/mandel/Main.java   |    8 +-
 .../com/amd/aparapi/sample/mandel/Main2D.java |  277 ++
 .../com/amd/aparapi/sample/squares/Main.java  |    4 +-
 test/codegen/build.xml                        |   33 +-
 33 files changed, 3872 insertions(+), 1384 deletions(-)
 create mode 100644 com.amd.aparapi/src/java/com/amd/aparapi/DeprecatedException.java
 create mode 100644 com.amd.aparapi/src/java/com/amd/aparapi/Range.java
 create mode 100644 com.amd.aparapi/src/java/com/amd/aparapi/RangeException.java
 create mode 100644 examples/nbody/local.bat
 create mode 100644 examples/nbody/src/com/amd/aparapi/examples/nbody/Local.java
 create mode 100644 samples/blackscholes/.classpath
 create mode 100644 samples/blackscholes/.project
 create mode 100644 samples/convolution/.classpath
 create mode 100644 samples/convolution/.project
 create mode 100644 samples/convolution/build.xml
 create mode 100644 samples/convolution/conv.bat
 create mode 100644 samples/convolution/src/com/amd/aparapi/sample/convolution/Main.java
 create mode 100644 samples/convolution/src/com/amd/aparapi/sample/convolution/Test12x4_4x2.java
 create mode 100644 samples/convolution/testcard.jpg
 create mode 100644 samples/mandel/mandel2D.bat
 create mode 100644 samples/mandel/mandel2D.sh
 create mode 100644 samples/mandel/src/com/amd/aparapi/sample/mandel/Main2D.java

diff --git a/build.xml b/build.xml
index ee646ad1..3b19d813 100644
--- a/build.xml
+++ b/build.xml
@@ -35,6 +35,7 @@
          <fileset dir="test" includes="*/build.xml"/>
       </subant>
       <delete dir="examples\nbody\jogamp"/> <!-- we handle the jogamp delete here, save downloading each build -->
+      <delete file="test\codegen\junit-4.10.jar"/> <!-- we handle the junit delete here, save downloading each build -->
       <ant dir="com.amd.aparapi.jni" target="clean"/> 
       <ant dir="com.amd.aparapi" target="clean"/> 
    </target>
diff --git a/com.amd.aparapi.jni/src/cpp/aparapi.cpp b/com.amd.aparapi.jni/src/cpp/aparapi.cpp
index 29fa4c27..4e9c4b31 100644
--- a/com.amd.aparapi.jni/src/cpp/aparapi.cpp
+++ b/com.amd.aparapi.jni/src/cpp/aparapi.cpp
@@ -1,40 +1,40 @@
 /*
-Copyright (c) 2010-2011, Advanced Micro Devices, Inc.
-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 the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
-
-If you use the software (in whole or in part), you shall adhere to all applicable U.S., European, and other export
-laws, including but not limited to the U.S. Export Administration Regulations ("EAR"), (15 C.F.R. Sections 730 through
-774), and E.U. Council Regulation (EC) No 1334/2000 of 22 June 2000.  Further, pursuant to Section 740.6 of the EAR,
-you hereby certify that, except pursuant to a license granted by the United States Department of Commerce Bureau of 
-Industry and Security or as otherwise permitted pursuant to a License Exception under the U.S. Export Administration 
-Regulations ("EAR"), you will not (1) export, re-export or release to a national of a country in Country Groups D:1,
-E:1 or E:2 any restricted technology, software, or source code you receive hereunder, or (2) export to Country Groups
-D:1, E:1 or E:2 the direct product of such technology or software, if such foreign produced direct product is subject
-to national security controls as identified on the Commerce Control List (currently found in Supplement 1 to Part 774
-of EAR).  For the most current Country Group listings, or for additional information about the EAR or your obligations
-under those regulations, please refer to the U.S. Bureau of Industry and Security’s website at http://www.bis.doc.gov/. 
-
-*/
+   Copyright (c) 2010-2011, Advanced Micro Devices, Inc.
+   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 the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+   If you use the software (in whole or in part), you shall adhere to all applicable U.S., European, and other export
+   laws, including but not limited to the U.S. Export Administration Regulations ("EAR"), (15 C.F.R. Sections 730 
+   through 774), and E.U. Council Regulation (EC) No 1334/2000 of 22 June 2000.  Further, pursuant to Section 740.6 of
+   the EAR, you hereby certify that, except pursuant to a license granted by the United States Department of Commerce
+   Bureau of Industry and Security or as otherwise permitted pursuant to a License Exception under the U.S. Export 
+   Administration Regulations ("EAR"), you will not (1) export, re-export or release to a national of a country in 
+   Country Groups D:1, E:1 or E:2 any restricted technology, software, or source code you receive hereunder, or (2) 
+   export to Country Groups D:1, E:1 or E:2 the direct product of such technology or software, if such foreign produced
+   direct product is subject to national security controls as identified on the Commerce Control List (currently 
+   found in Supplement 1 to Part 774 of EAR).  For the most current Country Group listings, or for additional 
+   information about the EAR or your obligations under those regulations, please refer to the U.S. Bureau of Industry
+   and Security’s website at http://www.bis.doc.gov/. 
+   */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -110,6 +110,10 @@ MicrosecondTimer timer;
 
 #include "com_amd_aparapi_KernelRunner.h"
 
+#define CHECK_NO_RETURN(condition, msg) if(condition){\
+   fprintf(stderr, "!!!!!!! %s failed !!!!!!!\n", msg);\
+}
+
 #define CHECK(condition, msg) if(condition){\
    fprintf(stderr, "!!!!!!! %s failed !!!!!!!\n", msg);\
    return 0;\
@@ -127,26 +131,9 @@ MicrosecondTimer timer;
 
 #define PRINT_CL_ERR(status, msg) fprintf(stderr, "!!!!!!! %s failed %s\n", msg, CLErrString(status));
 
-#define ASSERT_FIELD(id) CHECK(id##FieldID == 0, "No such field as " #id)
-
-#define GET_DEV_INFO(deviceId, param, val, format){\
-   status = clGetDeviceInfo(deviceId, param, sizeof(val), &(val), NULL);\
-   ASSERT_CL_NO_RETURN( "clGetDeviceInfo().");\
-   /*fprintf(stderr, #param " " format " \n", val);*/ \
-}
-
+#define ASSERT_FIELD(id) CHECK_NO_RETURN(id##FieldID == 0, "No such field as " #id)
 
-jfieldID typeFieldID;
-jfieldID isStaticFieldID;
-jfieldID nameFieldID;
-jfieldID javaArrayFieldID;
-jfieldID bytesPerLocalSizeFieldID;
-jfieldID sizeInBytesFieldID;
-jfieldID numElementsFieldID;
 
-// we rely on these being 0 initially to detect whether we have cached the above fieldId's 
-jclass clazz = (jclass)0;
-jclass argClazz = (jclass)0;
 
 static const char *CLErrString(cl_int status) {
    static struct { cl_int code; const char *msg; } error_table[] = {
@@ -214,7 +201,90 @@ static const char *CLErrString(cl_int status) {
 #endif
    return unknown;
 }
+class Range{
+   public:
+      static jclass rangeClazz;
+      static jfieldID globalSize_0_FieldID;
+      static jfieldID globalSize_1_FieldID;
+      static jfieldID globalSize_2_FieldID;
+      static jfieldID localSize_0_FieldID;
+      static jfieldID localSize_1_FieldID;
+      static jfieldID localSize_2_FieldID;
+      static jfieldID dimsFieldID;
+      static jfieldID localIsDerivedFieldID; 
+      jobject range;
+      cl_int dims;
+      size_t *offsets;
+      size_t *globalDims;
+      size_t *localDims;
+      jboolean localIsDerived;
+      Range(JNIEnv *jenv, jobject range):
+         range(range),
+         dims(0),
+         offsets(NULL),
+         globalDims(NULL),
+         localDims(NULL){
+            if (rangeClazz ==NULL){
+               jclass rangeClazz = jenv->GetObjectClass(range); 
+               globalSize_0_FieldID = jenv->GetFieldID(rangeClazz, "globalSize_0", "I"); ASSERT_FIELD(globalSize_0_);
+               globalSize_1_FieldID = jenv->GetFieldID(rangeClazz, "globalSize_1", "I"); ASSERT_FIELD(globalSize_1_);
+               globalSize_2_FieldID = jenv->GetFieldID(rangeClazz, "globalSize_2", "I"); ASSERT_FIELD(globalSize_2_);
+               localSize_0_FieldID = jenv->GetFieldID(rangeClazz, "localSize_0", "I"); ASSERT_FIELD(localSize_0_);
+               localSize_1_FieldID = jenv->GetFieldID(rangeClazz, "localSize_1", "I"); ASSERT_FIELD(localSize_1_);
+               localSize_2_FieldID = jenv->GetFieldID(rangeClazz, "localSize_2", "I"); ASSERT_FIELD(localSize_2_);
+               dimsFieldID = jenv->GetFieldID(rangeClazz, "dims", "I"); ASSERT_FIELD(dims);
+               localIsDerivedFieldID = jenv->GetFieldID(rangeClazz, "localIsDerived", "Z"); ASSERT_FIELD(localIsDerived);
+            }
+            dims = jenv->GetIntField(range, dimsFieldID);
+            localIsDerived = jenv->GetBooleanField(range, localIsDerivedFieldID);
+            if (dims >0){
+               //fprintf(stderr, "native range dims == %d\n", dims);
+               offsets = new size_t[dims];
+               globalDims = new size_t[dims];
+               localDims = new size_t[dims];
+               offsets[0]= 0;
+               localDims[0]= jenv->GetIntField(range, localSize_0_FieldID);
+               //fprintf(stderr, "native range localSize_0 == %d\n", localDims[0]);
+               globalDims[0]= jenv->GetIntField(range, globalSize_0_FieldID);
+               //fprintf(stderr, "native range globalSize_0 == %d\n", globalDims[0]);
+               if (dims >1){
+                  offsets[1]= 0;
+                  localDims[1]= jenv->GetIntField(range, localSize_1_FieldID);
+                  //fprintf(stderr, "native range localSize_1 == %d\n", localDims[1]);
+                  globalDims[1]= jenv->GetIntField(range, globalSize_1_FieldID);
+                  //fprintf(stderr, "native range globalSize_1 == %d\n", globalDims[1]);
+                  if (dims >2){
+                     offsets[2]= 0;
+                     localDims[2]= jenv->GetIntField(range, localSize_2_FieldID);
+                     //fprintf(stderr, "native range localSize_2 == %d\n", localDims[2]);
+                     globalDims[2]= jenv->GetIntField(range, globalSize_2_FieldID);
+                     //fprintf(stderr, "native range globalSize_2 == %d\n", globalDims[2]);
+                  }
+               }
 
+            }
+         }
+      ~Range(){
+         if (offsets!= NULL){
+            delete offsets;
+         }
+         if (globalDims!= NULL){
+            delete globalDims;
+         }
+         if (localDims!= NULL){
+            delete localDims;
+         }
+      }
+};
+jclass Range::rangeClazz = (jclass)0;
+jfieldID  Range::globalSize_0_FieldID=0;
+jfieldID  Range::globalSize_1_FieldID=0;
+jfieldID  Range::globalSize_2_FieldID=0;
+jfieldID  Range::localSize_0_FieldID=0;
+jfieldID  Range::localSize_1_FieldID=0;
+jfieldID  Range::localSize_2_FieldID=0;
+jfieldID  Range::dimsFieldID=0;
+jfieldID  Range::localIsDerivedFieldID=0; 
 
 class ProfileInfo{
    public:
@@ -243,13 +313,22 @@ class KernelArgRef{
 class JNIContext ; // forward reference
 
 class KernelArg{
+   private:
+      static jclass argClazz;
+      static jfieldID nameFieldID;
+      static jfieldID typeFieldID; 
+      static jfieldID isStaticFieldID; 
+      static jfieldID sizeInBytesFieldID;
+      static jfieldID numElementsFieldID; 
    public:
+      static jfieldID javaArrayFieldID; 
+      jobject argObj;
       char *name;        // used for debugging printfs
       jfieldID fieldID;  // The field that this arg represents in the kernel (java), used only for primitive updates
       jint type;         // a bit mask determining the type of this arg
       jboolean isStatic; // A flag indicating if the value is static
       jint sizeInBytes;  // bytes in the array or directBuf
-      jobject javaArg;   // global reference to the corresponding java KernelArg 
+      jobject javaArg;   // global reference to the corresponding java KernelArg object
       union{
          cl_char c;
          cl_double d;
@@ -259,6 +338,29 @@ class KernelArg{
          KernelArgRef ref;
       } value;
 
+      KernelArg(JNIEnv *jenv, jobject argObj):
+         argObj(argObj){
+            javaArg = jenv->NewGlobalRef(argObj);   // save a global ref to the java Arg Object
+            if (argClazz == 0){
+               jclass c = jenv->GetObjectClass(argObj); 
+               nameFieldID = jenv->GetFieldID(c, "name", "Ljava/lang/String;"); ASSERT_FIELD(name);
+               typeFieldID = jenv->GetFieldID(c, "type", "I"); ASSERT_FIELD(type);
+               isStaticFieldID = jenv->GetFieldID(c, "isStatic", "Z"); ASSERT_FIELD(isStatic);
+               javaArrayFieldID = jenv->GetFieldID(c, "javaArray", "Ljava/lang/Object;"); ASSERT_FIELD(javaArray);
+               sizeInBytesFieldID = jenv->GetFieldID(c, "sizeInBytes", "I"); ASSERT_FIELD(sizeInBytes);
+               numElementsFieldID = jenv->GetFieldID(c, "numElements", "I"); ASSERT_FIELD(numElements);
+            }
+            type = jenv->GetIntField(argObj, typeFieldID);
+            isStatic = jenv->GetBooleanField(argObj, isStaticFieldID);
+            jstring nameString  = (jstring)jenv->GetObjectField(argObj, nameFieldID);
+            const char *nameChars = jenv->GetStringUTFChars(nameString, NULL);
+            name=strdup(nameChars);
+            jenv->ReleaseStringUTFChars(nameString, nameChars);
+         }
+
+      ~KernelArg(){
+      }
+
       void unpinAbort(JNIEnv *jenv){
          jenv->ReleasePrimitiveArrayCritical((jarray)value.ref.javaArray, value.ref.addr,JNI_ABORT);
       }
@@ -342,9 +444,6 @@ class KernelArg{
       int isAparapiBufHasArray(){
          return (type&com_amd_aparapi_KernelRunner_ARG_APARAPI_BUF_HAS_ARRAY);
       }
-      int isAparapiBufIsDirect(){
-         return (type&com_amd_aparapi_KernelRunner_ARG_APARAPI_BUF_IS_DIRECT);
-      }
       int isBackedByArray(){
          return ( (isArray() && isGlobal()) || ((isGlobal() || isConstant()) && isAparapiBufHasArray()));
       }
@@ -354,8 +453,27 @@ class KernelArg{
       int mustWriteBuffer(){
          return ((isImplicit()&&isRead()&&!isConstant())||(isExplicit()&&isExplicitWrite()));
       }
-
+      void syncType(JNIEnv* jenv){
+         type = jenv->GetIntField(javaArg, typeFieldID);
+      }
+      void syncSizeInBytes(JNIEnv* jenv){
+         sizeInBytes = jenv->GetIntField(javaArg, sizeInBytesFieldID);
+      }
+      void syncJavaArrayLength(JNIEnv* jenv){
+         value.ref.javaArrayLength = jenv->GetIntField(javaArg, numElementsFieldID);
+      }
+      void clearExplicitBufferBit(JNIEnv* jenv){
+         type &= ~com_amd_aparapi_KernelRunner_ARG_EXPLICIT_WRITE;
+         jenv->SetIntField(javaArg, typeFieldID,type );
+      }
 };
+jclass KernelArg::argClazz=(jclass)0;
+jfieldID KernelArg::nameFieldID=0;
+jfieldID KernelArg::typeFieldID=0; 
+jfieldID KernelArg::isStaticFieldID=0; 
+jfieldID KernelArg::javaArrayFieldID=0; 
+jfieldID KernelArg::sizeInBytesFieldID=0;
+jfieldID KernelArg::numElementsFieldID=0; 
 
 class JNIContext{
    private: 
@@ -365,10 +483,7 @@ class JNIContext{
       cl_platform_id* platforms;
       cl_uint platformc;
    public:
-      JNIEnv *jenv;
       jobject kernelObject;
-      jint numProcessors;
-      jint maxJTPLocalSize;
       jclass kernelClass;
       cl_uint deviceIdc;
       cl_device_id* deviceIds;
@@ -391,6 +506,7 @@ class JNIContext{
       // these map to camelCase form of CL_DEVICE_XXX_XXX  For example CL_DEVICE_MAX_COMPUTE_UNITS == maxComputeUnits
       cl_uint maxComputeUnits;
       cl_uint maxWorkItemDimensions;
+      size_t *maxWorkItemSizes;
       size_t maxWorkGroupSize;
       cl_ulong globalMemSize;
       cl_ulong localMemSize;
@@ -399,13 +515,10 @@ class JNIContext{
          return((JNIContext*)jniContextHandle);
       }
 
-      JNIContext(JNIEnv *_jenv, jobject _kernelObject, jint _flags, jint _numProcessors, jint _maxJTPLocalSize): 
-         jenv(_jenv),
+      JNIContext(JNIEnv *jenv, jobject _kernelObject, jint _flags): 
          kernelObject(jenv->NewGlobalRef(_kernelObject)),
          kernelClass((jclass)jenv->NewGlobalRef(jenv->GetObjectClass(_kernelObject))), 
          flags(_flags),
-         numProcessors(_numProcessors),
-         maxJTPLocalSize(_maxJTPLocalSize),
          platform(NULL),
          profileBaseTime(0),
          deviceType(((flags&com_amd_aparapi_KernelRunner_JNI_FLAG_USE_GPU)==com_amd_aparapi_KernelRunner_JNI_FLAG_USE_GPU)?CL_DEVICE_TYPE_GPU:CL_DEVICE_TYPE_CPU),
@@ -438,1179 +551,1209 @@ class JNIContext{
                      // platformVersionName = "OpenCL 1.1 AMD-APP-SDK-v2.5 (684.213)"|"OpenCL 1.1 CUDA 4.0.1"
 #ifndef __APPLE__
                      // Here we check if the platformVersionName starts with "OpenCL 1.1" (10 chars!) 
-                     if (!strncmp(platformVersionName, "OpenCL 1.1", 10)) {
+                     if (!strncmp(platformVersionName, "OpenCL 1.1", 10)) { //}
 #else 
-                        // Here we check if the platformVersionName starts with "OpenCL 1.1" or "OpenCL 1.0" (10 chars!) 
-                        if (!strncmp(platformVersionName, "OpenCL 1.1", 10) || !strncmp(platformVersionName, "OpenCL 1.0", 10)) {
+                     // Here we check if the platformVersionName starts with "OpenCL 1.1" or "OpenCL 1.0" (10 chars!) 
+                     if (!strncmp(platformVersionName, "OpenCL 1.1", 10) || !strncmp(platformVersionName, "OpenCL 1.0", 10)) { // }
 #endif
-                           // Get the # of devices
-                           status = clGetDeviceIDs(platforms[i], deviceType, 0, NULL, &deviceIdc);
-                           // now check if this platform supports the requested device type (GPU or CPU)
-                           if (status == CL_SUCCESS && deviceIdc >0 ){
-                              if (deviceIdc >1){
-                                 if (isVerbose()){
-                                    fprintf(stderr, "Warning attempt to use %d devices\n", deviceIdc);
-                                 }
-                                 deviceIdc = 1; // Hack to work around issue #18 (multiple device error)
-                                 if (isVerbose()){
-                                    fprintf(stderr, "Locking deviceIdc to %d to work around issue #18\n", deviceIdc);
-                                 }
-                              }
-                              platform = platforms[i];
-                              if (isVerbose()){
-                                 fprintf(stderr, "platform %s supports requested device type\n", platformVendorName);
-                              }
-
-                              deviceIds = new cl_device_id[deviceIdc];
-                              status = clGetDeviceIDs(platform, deviceType, deviceIdc, deviceIds, NULL);
-                              if (status == CL_SUCCESS){
-                                 ASSERT_CL_NO_RETURN("clGetDeviceIDs()"); 
-
-                                 GET_DEV_INFO(deviceIds[0], CL_DEVICE_MAX_COMPUTE_UNITS, maxComputeUnits, "%d");
-                                 GET_DEV_INFO(deviceIds[0], CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, maxWorkItemDimensions, "%d");
-                                 GET_DEV_INFO(deviceIds[0], CL_DEVICE_MAX_WORK_GROUP_SIZE, maxWorkGroupSize, "%d");
-                                 GET_DEV_INFO(deviceIds[0], CL_DEVICE_GLOBAL_MEM_SIZE, globalMemSize, "%d");
-                                 GET_DEV_INFO(deviceIds[0], CL_DEVICE_LOCAL_MEM_SIZE, localMemSize, "%d");
-                                 if (isVerbose()){
-
-                                    fprintf(stderr, "device[%p]: Type: ", deviceIds[0]);
-                                    if (deviceType & CL_DEVICE_TYPE_DEFAULT) {
-                                       //  deviceType &= ~CL_DEVICE_TYPE_DEFAULT;
-                                       fprintf(stderr, "Default ");
-                                    }else if (deviceType & CL_DEVICE_TYPE_CPU) {
-                                       // deviceType &= ~CL_DEVICE_TYPE_CPU;
-                                       fprintf(stderr, "CPU ");
-                                    }else if (deviceType & CL_DEVICE_TYPE_GPU) {
-                                       // deviceType &= ~CL_DEVICE_TYPE_GPU;
-                                       fprintf(stderr, "GPU ");
-                                    }else if (deviceType & CL_DEVICE_TYPE_ACCELERATOR) {
-                                       // deviceType &= ~CL_DEVICE_TYPE_ACCELERATOR;
-                                       fprintf(stderr, "Accelerator ");
-                                    }else{
-                                       fprintf(stderr, "Unknown (0x%llx) ", deviceType);
-                                    }
-                                    fprintf(stderr, "\n");
-                                 }
-                                 cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0 };
-                                 cl_context_properties* cprops = (NULL == platform) ? NULL : cps;
-                                 context = clCreateContextFromType( cprops, deviceType, NULL, NULL, &status);
-                                 ASSERT_CL_NO_RETURN("clCreateContextFromType()");
-                                 if (status == CL_SUCCESS){
-
-                                    valid = JNI_TRUE;
-                                 }
-                              }
-                           }else{
-                              if (isVerbose()){
-                                 fprintf(stderr, "platform %s does not support requested device type skipping!\n", platformVendorName);
-                              }
+                     // Get the # of devices
+                     status = clGetDeviceIDs(platforms[i], deviceType, 0, NULL, &deviceIdc);
+                     // now check if this platform supports the requested device type (GPU or CPU)
+                     if (status == CL_SUCCESS && deviceIdc >0 ){
+                        if (deviceIdc >1){
+                           if (isVerbose()){
+                              fprintf(stderr, "Warning attempt to use %d devices\n", deviceIdc);
                            }
-
-                        }else{
+                           deviceIdc = 1; // Hack to work around issue #18 (multiple device error)
                            if (isVerbose()){
-#ifndef __APPLE__
-                              fprintf(stderr, "platform %s version %s is not OpenCL 1.1 skipping!\n", platformVendorName, platformVersionName);
-#else
-                              fprintf(stderr, "platform %s version %s is neither OpenCL 1.1 or OpenCL 1.0 skipping!\n", platformVendorName, platformVersionName);
-#endif
-
+                              fprintf(stderr, "Locking deviceIdc to %d to work around issue #18\n", deviceIdc);
                            }
                         }
-                     }
+                        platform = platforms[i];
+                        if (isVerbose()){
+                           fprintf(stderr, "platform %s supports requested device type\n", platformVendorName);
+                        }
 
-                  } 
-               }else{
-                  if (isVerbose()){
-                     fprintf(stderr, "no opencl platforms available!\n");
-                  }
-               }
+                        deviceIds = new cl_device_id[deviceIdc];
+                        status = clGetDeviceIDs(platform, deviceType, deviceIdc, deviceIds, NULL);
+                        ASSERT_CL_NO_RETURN("clGetDeviceIDs()"); 
+                        if (status == CL_SUCCESS){
 
-            }
+                           status = clGetDeviceInfo(deviceIds[0], CL_DEVICE_MAX_COMPUTE_UNITS,  sizeof(maxComputeUnits), &maxComputeUnits, NULL);
+                           ASSERT_CL_NO_RETURN( "clGetDeviceInfo(CL_DEVICE_MAX_COMPUTE_UNITS).");
 
-            jboolean isValid(){
-               return(valid);
-            }
-            jboolean isProfilingEnabled(){
-               return((flags&com_amd_aparapi_KernelRunner_JNI_FLAG_ENABLE_PROFILING)==com_amd_aparapi_KernelRunner_JNI_FLAG_ENABLE_PROFILING?JNI_TRUE:JNI_FALSE);
-            }
-            jboolean isUsingGPU(){
-               return((flags&com_amd_aparapi_KernelRunner_JNI_FLAG_USE_GPU)==com_amd_aparapi_KernelRunner_JNI_FLAG_USE_GPU?JNI_TRUE:JNI_FALSE);
-            }
-            jboolean isVerbose(){
-               return((flags&com_amd_aparapi_KernelRunner_JNI_FLAG_ENABLE_VERBOSE_JNI)==com_amd_aparapi_KernelRunner_JNI_FLAG_ENABLE_VERBOSE_JNI?JNI_TRUE:JNI_FALSE);
-            }
+                           status = clGetDeviceInfo(deviceIds[0], CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS,  sizeof(maxWorkItemDimensions), &maxWorkItemDimensions, NULL);
+                           ASSERT_CL_NO_RETURN( "clGetDeviceInfo(CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS).");
 
-            ~JNIContext(){
-               cl_int status = CL_SUCCESS;
-               jenv->DeleteGlobalRef(kernelObject);
-               jenv->DeleteGlobalRef(kernelClass);
-               if (context != 0){
-                  status = clReleaseContext(context);
-                  ASSERT_CL_NO_RETURN("clReleaseContext()");
-                  context = (cl_context)0;
-               }
-               if (commandQueues){
-                  for (int dev=0; dev<deviceIdc; dev++){
-                     status = clReleaseCommandQueue((cl_command_queue)commandQueues[dev]);
-                     ASSERT_CL_NO_RETURN("clReleaseCommandQueue()");
-                     commandQueues[dev] = (cl_command_queue)0;
-                  }
-                  delete[] commandQueues; commandQueues = NULL;
-               }
-               if (program != 0){
-                  status = clReleaseProgram((cl_program)program);
-                  ASSERT_CL_NO_RETURN("clReleaseProgram()");
-                  program = (cl_program)0;
-               }
-               if (kernel != 0){
-                  status = clReleaseKernel((cl_kernel)kernel);
-                  ASSERT_CL_NO_RETURN("clReleaseKernel()");
-                  kernel = (cl_kernel)0;
-               }
-               if (platforms){
-                  delete []platforms; platforms=NULL;
-               }
-               if (deviceIds){
-                  delete [] deviceIds; deviceIds=NULL;
-               }
-               if (argc> 0){
-                  for (int i=0; i< argc; i++){
-                     KernelArg *arg = args[i];
-                     if (!arg->isPrimitive()){
-                        if (arg->value.ref.mem != 0){
-                           status = clReleaseMemObject((cl_mem)arg->value.ref.mem);
-                           ASSERT_CL_NO_RETURN("clReleaseMemObject()");
-                           arg->value.ref.mem = (cl_mem)0;
+                           maxWorkItemSizes = (size_t *)malloc(sizeof(size_t)*maxWorkItemDimensions);
+                           status = clGetDeviceInfo(deviceIds[0], CL_DEVICE_MAX_WORK_ITEM_SIZES,  sizeof(size_t)*maxWorkItemDimensions, maxWorkItemSizes, NULL);
+
+                           ASSERT_CL_NO_RETURN( "clGetDeviceInfo(CL_DEVICE_MAX_WORK_ITEM_SIZES).");
+
+                           status = clGetDeviceInfo(deviceIds[0], CL_DEVICE_MAX_WORK_GROUP_SIZE,  sizeof(maxWorkGroupSize), &maxWorkGroupSize, NULL);
+                           ASSERT_CL_NO_RETURN( "clGetDeviceInfo(CL_DEVICE_MAX_WORK_GROUP_SIZE).");
+
+                           status = clGetDeviceInfo(deviceIds[0], CL_DEVICE_GLOBAL_MEM_SIZE,  sizeof(globalMemSize), &globalMemSize, NULL);
+                           ASSERT_CL_NO_RETURN( "clGetDeviceInfo(CL_DEVICE_GLOBAL_MEM_SIZE).");
+
+                           status = clGetDeviceInfo(deviceIds[0], CL_DEVICE_LOCAL_MEM_SIZE,  sizeof(localMemSize), &localMemSize, NULL);
+
+                           ASSERT_CL_NO_RETURN( "clGetDeviceInfo(CL_DEVICE_LOCAL_MEM_SIZE).");
+
+
+                           if (isVerbose()){
+                              fprintf(stderr, "device[%p]: Type: ", deviceIds[0]);
+                              if (deviceType & CL_DEVICE_TYPE_DEFAULT) {
+                                 //  deviceType &= ~CL_DEVICE_TYPE_DEFAULT;
+                                 fprintf(stderr, "Default ");
+                              }else if (deviceType & CL_DEVICE_TYPE_CPU) {
+                                 // deviceType &= ~CL_DEVICE_TYPE_CPU;
+                                 fprintf(stderr, "CPU ");
+                              }else if (deviceType & CL_DEVICE_TYPE_GPU) {
+                                 // deviceType &= ~CL_DEVICE_TYPE_GPU;
+                                 fprintf(stderr, "GPU ");
+                              }else if (deviceType & CL_DEVICE_TYPE_ACCELERATOR) {
+                                 // deviceType &= ~CL_DEVICE_TYPE_ACCELERATOR;
+                                 fprintf(stderr, "Accelerator ");
+                              }else{
+                                 fprintf(stderr, "Unknown (0x%llx) ", deviceType);
+                              }
+                              fprintf(stderr, "\n");
+                           }
+                           cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0 };
+                           cl_context_properties* cprops = (NULL == platform) ? NULL : cps;
+                           context = clCreateContextFromType( cprops, deviceType, NULL, NULL, &status);
+                           ASSERT_CL_NO_RETURN("clCreateContextFromType()");
+                           if (status == CL_SUCCESS){
+
+                              valid = JNI_TRUE;
+                           }
                         }
-                        if (arg->value.ref.javaArray != NULL)  {
-                           jenv->DeleteWeakGlobalRef((jweak) arg->value.ref.javaArray);
+                     }else{
+                        if (isVerbose()){
+                           fprintf(stderr, "platform %s does not support requested device type skipping!\n", platformVendorName);
                         }
                      }
-                     if (arg->name != NULL){
-                        free(arg->name); arg->name = NULL;
-                     }
-                     if (arg->javaArg != NULL ) {
-                        jenv->DeleteGlobalRef((jobject) arg->javaArg);
-                     }
-                     delete arg; arg=args[i]=NULL;
-                  }
-                  delete[] args; args=NULL;
 
-                  delete []readEvents; readEvents =NULL;
-                  delete []writeEvents; writeEvents = NULL;
-                  delete []executeEvents; executeEvents = NULL;
+                  }else{
+                     if (isVerbose()){
+#ifndef __APPLE__
+                        fprintf(stderr, "platform %s version %s is not OpenCL 1.1 skipping!\n", platformVendorName, platformVersionName);
+#else
+                        fprintf(stderr, "platform %s version %s is neither OpenCL 1.1 or OpenCL 1.0 skipping!\n", platformVendorName, platformVersionName);
+#endif
 
-                  if (isProfilingEnabled()) {
-                     if (profileFile != NULL && profileFile != stderr) {
-                        fclose(profileFile);
                      }
-                     delete[] readEventArgs; readEventArgs=0;
-                     delete[] writeEventArgs; writeEventArgs=0;
-                  } 
-               }
-            }
-
-            /*
-               Release JNI critical pinned arrays before returning to java code
-               */
-            void unpinAll() {
-               for (int i=0; i< argc; i++){
-                  KernelArg *arg = args[i];
-                  if (arg->isBackedByArray()) {
-                     arg->unpin(jenv);
                   }
                }
+
+            } 
+         }else{
+            if (isVerbose()){
+               fprintf(stderr, "no opencl platforms available!\n");
             }
+         }
 
+}
 
-         };
+jboolean isValid(){
+   return(valid);
+}
+jboolean isProfilingEnabled(){
+   return((flags&com_amd_aparapi_KernelRunner_JNI_FLAG_ENABLE_PROFILING)==com_amd_aparapi_KernelRunner_JNI_FLAG_ENABLE_PROFILING?JNI_TRUE:JNI_FALSE);
+}
+jboolean isUsingGPU(){
+   return((flags&com_amd_aparapi_KernelRunner_JNI_FLAG_USE_GPU)==com_amd_aparapi_KernelRunner_JNI_FLAG_USE_GPU?JNI_TRUE:JNI_FALSE);
+}
+jboolean isVerbose(){
+   return((flags&com_amd_aparapi_KernelRunner_JNI_FLAG_ENABLE_VERBOSE_JNI)==com_amd_aparapi_KernelRunner_JNI_FLAG_ENABLE_VERBOSE_JNI?JNI_TRUE:JNI_FALSE);
+}
 
-      jclass cacheKernelArgFields(JNIEnv *jenv, jobject jobj){
-         jclass c = jenv->GetObjectClass(jobj); 
-         nameFieldID = jenv->GetFieldID(c, "name", "Ljava/lang/String;"); ASSERT_FIELD(name);
-         typeFieldID = jenv->GetFieldID(c, "type", "I"); ASSERT_FIELD(type);
-         isStaticFieldID = jenv->GetFieldID(c, "isStatic", "Z"); ASSERT_FIELD(isStatic);
-         javaArrayFieldID = jenv->GetFieldID(c, "javaArray", "Ljava/lang/Object;"); ASSERT_FIELD(javaArray);
-         bytesPerLocalSizeFieldID = jenv->GetFieldID(c, "bytesPerLocalSize", "I"); ASSERT_FIELD(bytesPerLocalSize);
-         sizeInBytesFieldID = jenv->GetFieldID(c, "sizeInBytes", "I"); ASSERT_FIELD(sizeInBytes);
-         numElementsFieldID = jenv->GetFieldID(c, "numElements", "I"); ASSERT_FIELD(numElements);
-         return(c);
-      }
+~JNIContext(){
+}
 
-      JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_disposeJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle) {
-         cl_int status = CL_SUCCESS;
-         JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
-         if (jniContext != NULL){
-            delete jniContext;//free(jniContext);
-            jniContext = NULL;
-         }
-         return(status);
+void dispose(JNIEnv *jenv){
+   cl_int status = CL_SUCCESS;
+   jenv->DeleteGlobalRef(kernelObject);
+   jenv->DeleteGlobalRef(kernelClass);
+   if (context != 0){
+      status = clReleaseContext(context);
+      ASSERT_CL_NO_RETURN("clReleaseContext()");
+      context = (cl_context)0;
+   }
+   if (commandQueues){
+      for (int dev=0; dev<deviceIdc; dev++){
+         status = clReleaseCommandQueue((cl_command_queue)commandQueues[dev]);
+         ASSERT_CL_NO_RETURN("clReleaseCommandQueue()");
+         commandQueues[dev] = (cl_command_queue)0;
       }
-
-      void idump(char *str, void *ptr, int size){
-         int * iptr = (int *)ptr;
-         for (int i=0; i<size/sizeof(int); i++){
-            fprintf(stderr, "%s%4d %d\n", str, i, iptr[i]);
+      delete[] commandQueues; commandQueues = NULL;
+   }
+   if (program != 0){
+      status = clReleaseProgram((cl_program)program);
+      ASSERT_CL_NO_RETURN("clReleaseProgram()");
+      program = (cl_program)0;
+   }
+   if (kernel != 0){
+      status = clReleaseKernel((cl_kernel)kernel);
+      ASSERT_CL_NO_RETURN("clReleaseKernel()");
+      kernel = (cl_kernel)0;
+   }
+   if (platforms){
+      delete []platforms; platforms=NULL;
+   }
+   if (deviceIds){
+      delete [] deviceIds; deviceIds=NULL;
+   }
+   if (argc> 0){
+      for (int i=0; i< argc; i++){
+         KernelArg *arg = args[i];
+         if (!arg->isPrimitive()){
+            if (arg->value.ref.mem != 0){
+               status = clReleaseMemObject((cl_mem)arg->value.ref.mem);
+               ASSERT_CL_NO_RETURN("clReleaseMemObject()");
+               arg->value.ref.mem = (cl_mem)0;
+            }
+            if (arg->value.ref.javaArray != NULL)  {
+               jenv->DeleteWeakGlobalRef((jweak) arg->value.ref.javaArray);
+            }
+         }
+         if (arg->name != NULL){
+            free(arg->name); arg->name = NULL;
          }
+         if (arg->javaArg != NULL ) {
+            jenv->DeleteGlobalRef((jobject) arg->javaArg);
+         }
+         delete arg; arg=args[i]=NULL;
       }
+      delete[] args; args=NULL;
+
+      delete []readEvents; readEvents =NULL;
+      delete []writeEvents; writeEvents = NULL;
+      delete []executeEvents; executeEvents = NULL;
 
-      void fdump(char *str, void *ptr, int size){
-         float * fptr = (float *)ptr;
-         for (int i=0; i<size/sizeof(float); i++){
-            fprintf(stderr, "%s%4d %6.2f\n", str, i, fptr[i]);
+      if (isProfilingEnabled()) {
+         if (profileFile != NULL && profileFile != stderr) {
+            fclose(profileFile);
          }
+         delete[] readEventArgs; readEventArgs=0;
+         delete[] writeEventArgs; writeEventArgs=0;
+      } 
+   }
+}
+
+/*
+   Release JNI critical pinned arrays before returning to java code
+   */
+void unpinAll(JNIEnv* jenv) {
+   for (int i=0; i< argc; i++){
+      KernelArg *arg = args[i];
+      if (arg->isBackedByArray()) {
+         arg->unpin(jenv);
       }
+   }
+}
 
 
-      jint writeProfileInfo(JNIContext* jniContext){
-         cl_ulong currSampleBaseTime = -1;
-         int pos = 1;
+};
 
-         if (jniContext->firstRun) {
-            fprintf(jniContext->profileFile, "# PROFILE Name, queued, submit, start, end (microseconds)\n");
-         }       
 
-         // A read by a user kernel means the OpenCL layer wrote to the kernel and vice versa
-         for (int i=0; i< jniContext->argc; i++){
-            KernelArg *arg=jniContext->args[i];
-            if (arg->isBackedByArray() && arg->isRead()){
 
-               // Initialize the base time for this sample
-               if (currSampleBaseTime == -1) {
-                  currSampleBaseTime = arg->value.ref.write.queued;
-               } 
 
-               if (jniContext->profileBaseTime == 0){
-                  jniContext->profileBaseTime = arg->value.ref.write.queued;
+JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_disposeJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle) {
+   cl_int status = CL_SUCCESS;
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext != NULL){
+      jniContext->dispose(jenv);
+      delete jniContext;
+      jniContext = NULL;
+   }
+   return(status);
+}
 
-                  // Write the base time as the first item in the csv
-                  //fprintf(jniContext->profileFile, "%llu,", jniContext->profileBaseTime);
-               }
+void idump(char *str, void *ptr, int size){
+   int * iptr = (int *)ptr;
+   for (int i=0; i<size/sizeof(int); i++){
+      fprintf(stderr, "%s%4d %d\n", str, i, iptr[i]);
+   }
+}
 
-               fprintf(jniContext->profileFile, "%d write %s,", pos++, arg->name);
+void fdump(char *str, void *ptr, int size){
+   float * fptr = (float *)ptr;
+   for (int i=0; i<size/sizeof(float); i++){
+      fprintf(stderr, "%s%4d %6.2f\n", str, i, fptr[i]);
+   }
+}
 
-               fprintf(jniContext->profileFile, "%lu,%lu,%lu,%lu,",  
-                     (arg->value.ref.write.queued - currSampleBaseTime)/1000, 
-                     (arg->value.ref.write.submit - currSampleBaseTime)/1000, 
-                     (arg->value.ref.write.start - currSampleBaseTime)/1000, 
-                     (arg->value.ref.write.end - currSampleBaseTime)/1000);
-            }
-         }
 
-         if (jniContext->profileBaseTime == 0){
-            jniContext->profileBaseTime = jniContext->exec.queued;
+jint writeProfileInfo(JNIContext* jniContext){
+   cl_ulong currSampleBaseTime = -1;
+   int pos = 1;
 
-            // Write the base time as the first item in the csv
-            //fprintf(jniContext->profileFile, "%llu,", jniContext->profileBaseTime);
-         }
+   if (jniContext->firstRun) {
+      fprintf(jniContext->profileFile, "# PROFILE Name, queued, submit, start, end (microseconds)\n");
+   }       
 
-         // Initialize the base time for this sample if necessary
+   // A read by a user kernel means the OpenCL layer wrote to the kernel and vice versa
+   for (int i=0; i< jniContext->argc; i++){
+      KernelArg *arg=jniContext->args[i];
+      if (arg->isBackedByArray() && arg->isRead()){
+
+         // Initialize the base time for this sample
          if (currSampleBaseTime == -1) {
-            currSampleBaseTime = jniContext->exec.queued;
+            currSampleBaseTime = arg->value.ref.write.queued;
          } 
 
-         // exec 
-         fprintf(jniContext->profileFile, "%d exec,", pos++);
-
-         fprintf(jniContext->profileFile, "%lu,%lu,%lu,%lu,",  
-               (jniContext->exec.queued - currSampleBaseTime)/1000, 
-               (jniContext->exec.submit - currSampleBaseTime)/1000, 
-               (jniContext->exec.start - currSampleBaseTime)/1000, 
-               (jniContext->exec.end - currSampleBaseTime)/1000);
-
-         // 
-         if ( jniContext->argc == 0 ) {
-            fprintf(jniContext->profileFile, "\n");
-         } else { 
-            for (int i=0; i< jniContext->argc; i++){
-               KernelArg *arg=jniContext->args[i];
-               if (arg->isBackedByArray() && arg->isWrite()){
-                  if (jniContext->profileBaseTime == 0){
-                     jniContext->profileBaseTime = arg->value.ref.read.queued;
-
-                     // Write the base time as the first item in the csv
-                     //fprintf(jniContext->profileFile, "%llu,", jniContext->profileBaseTime);               
-                  }
-
-                  // Initialize the base time for this sample
-                  if (currSampleBaseTime == -1) {
-                     currSampleBaseTime = arg->value.ref.read.queued;
-                  }
-
-                  fprintf(jniContext->profileFile, "%d read %s,", pos++, arg->name);
+         if (jniContext->profileBaseTime == 0){
+            jniContext->profileBaseTime = arg->value.ref.write.queued;
 
-                  fprintf(jniContext->profileFile, "%lu,%lu,%lu,%lu,",  
-                        (arg->value.ref.read.queued - currSampleBaseTime)/1000, 
-                        (arg->value.ref.read.submit - currSampleBaseTime)/1000, 
-                        (arg->value.ref.read.start - currSampleBaseTime)/1000, 
-                        (arg->value.ref.read.end - currSampleBaseTime)/1000);
-               }
-            }
+            // Write the base time as the first item in the csv
+            //fprintf(jniContext->profileFile, "%llu,", jniContext->profileBaseTime);
          }
-         fprintf(jniContext->profileFile, "\n");
-         return(0);
-      }
-
-      // Should failed profiling abort the run and return early?
-      cl_int profile(ProfileInfo *profileInfo, cl_event *event){
-         cl_int status = CL_SUCCESS;
-         status = clGetEventProfilingInfo(*event, CL_PROFILING_COMMAND_QUEUED, sizeof(profileInfo->queued), &(profileInfo->queued), NULL);
-         ASSERT_CL( "clGetEventProfiliningInfo() QUEUED");
-         status = clGetEventProfilingInfo(*event, CL_PROFILING_COMMAND_SUBMIT, sizeof(profileInfo->submit), &(profileInfo->submit), NULL);
-         ASSERT_CL( "clGetEventProfiliningInfo() SUBMIT");
-         status = clGetEventProfilingInfo(*event, CL_PROFILING_COMMAND_START, sizeof(profileInfo->start), &(profileInfo->start), NULL);
-         ASSERT_CL( "clGetEventProfiliningInfo() START");
-         status = clGetEventProfilingInfo(*event, CL_PROFILING_COMMAND_END, sizeof(profileInfo->end), &(profileInfo->end), NULL);
-         ASSERT_CL( "clGetEventProfiliningInfo() END");
-         return status;
-      }
-
 
+         fprintf(jniContext->profileFile, "%d write %s,", pos++, arg->name);
 
+         fprintf(jniContext->profileFile, "%lu,%lu,%lu,%lu,",  
+               (arg->value.ref.write.queued - currSampleBaseTime)/1000, 
+               (arg->value.ref.write.submit - currSampleBaseTime)/1000, 
+               (arg->value.ref.write.start - currSampleBaseTime)/1000, 
+               (arg->value.ref.write.end - currSampleBaseTime)/1000);
+      }
+   }
 
-      jint updateKernel(JNIEnv *jenv, jobject jobj, JNIContext* jniContext) {
-         cl_int status = CL_SUCCESS;
-         if (jniContext != NULL){
-            // we need to step through the array of KernelArg's to create the info required to create the cl_mem buffers.
-            for (jint i=0; i<jniContext->argc; i++){ 
-               KernelArg *arg=jniContext->args[i];
-
-               arg->type = jenv->GetIntField(arg->javaArg, typeFieldID);
-               if (jniContext->isVerbose()){
-                  fprintf(stderr, "got type for %s: %08x\n", arg->name, arg->type);
-               }
-               if (!arg->isPrimitive()) {
-                  // Following used for all primitive arrays, object arrays  and nio Buffers
-                  jarray newRef = (jarray)jenv->GetObjectField(arg->javaArg, javaArrayFieldID);
-                  if (jniContext->isVerbose()){
-
-                     fprintf(stderr, "testing for Resync javaArray %s: old=%p, new=%p\n", arg->name, arg->value.ref.javaArray, newRef);         
-                  }
-
-                  jboolean isSame = jenv->IsSameObject( newRef, arg->value.ref.javaArray);
-                  if (isSame == JNI_FALSE) {
-                     if (jniContext->isVerbose()){
-                        fprintf(stderr, "Resync javaArray for %s: %p  %p\n", arg->name, newRef, arg->value.ref.javaArray);         
-                     }
-                     // Free previous ref if any
-                     if (arg->value.ref.javaArray != NULL) {
-                        jenv->DeleteWeakGlobalRef((jweak) arg->value.ref.javaArray);
-                        if (jniContext->isVerbose()){
-                           fprintf(stderr, "DeleteWeakGlobalRef for %s: %p\n", arg->name, arg->value.ref.javaArray);         
-                        }
-                     }
-
-                     // need to free opencl buffers, run will reallocate later
-                     if (arg->value.ref.mem != 0) {
-                        //fprintf(stderr, "-->releaseMemObject[%d]\n", i);
-                        status = clReleaseMemObject((cl_mem)arg->value.ref.mem);
-                        //fprintf(stderr, "<--releaseMemObject[%d]\n", i);
-                        ASSERT_CL("clReleaseMemObject()");
-                        arg->value.ref.mem = (cl_mem)0;
-                     }
-
-                     arg->value.ref.mem = (cl_mem) 0;
-                     arg->value.ref.addr = NULL;
+   if (jniContext->profileBaseTime == 0){
+      jniContext->profileBaseTime = jniContext->exec.queued;
 
-                     // Capture new array ref from the kernel arg object
+      // Write the base time as the first item in the csv
+      //fprintf(jniContext->profileFile, "%llu,", jniContext->profileBaseTime);
+   }
 
-                     if (newRef != NULL) {
-                        arg->value.ref.javaArray = (jarray)jenv->NewWeakGlobalRef((jarray)newRef);
-                        if (jniContext->isVerbose()){
-                           fprintf(stderr, "NewWeakGlobalRef for %s, set to %p\n", arg->name,
-                                 arg->value.ref.javaArray);         
-                        }
-                     } else {
-                        arg->value.ref.javaArray = NULL;
-                     }
-                     arg->value.ref.isArray = !arg->isAparapiBufIsDirect();
+   // Initialize the base time for this sample if necessary
+   if (currSampleBaseTime == -1) {
+      currSampleBaseTime = jniContext->exec.queued;
+   } 
+
+   // exec 
+   fprintf(jniContext->profileFile, "%d exec,", pos++);
+
+   fprintf(jniContext->profileFile, "%lu,%lu,%lu,%lu,",  
+         (jniContext->exec.queued - currSampleBaseTime)/1000, 
+         (jniContext->exec.submit - currSampleBaseTime)/1000, 
+         (jniContext->exec.start - currSampleBaseTime)/1000, 
+         (jniContext->exec.end - currSampleBaseTime)/1000);
+
+   // 
+   if ( jniContext->argc == 0 ) {
+      fprintf(jniContext->profileFile, "\n");
+   } else { 
+      for (int i=0; i< jniContext->argc; i++){
+         KernelArg *arg=jniContext->args[i];
+         if (arg->isBackedByArray() && arg->isWrite()){
+            if (jniContext->profileBaseTime == 0){
+               jniContext->profileBaseTime = arg->value.ref.read.queued;
+
+               // Write the base time as the first item in the csv
+               //fprintf(jniContext->profileFile, "%llu,", jniContext->profileBaseTime);               
+            }
 
-                     // Save the sizeInBytes which was set on the java side
-                     arg->sizeInBytes = jenv->GetIntField(arg->javaArg, sizeInBytesFieldID);
+            // Initialize the base time for this sample
+            if (currSampleBaseTime == -1) {
+               currSampleBaseTime = arg->value.ref.read.queued;
+            }
 
-                     if (jniContext->isVerbose()){
-                        fprintf(stderr, "updateKernel, args[%d].sizeInBytes=%d\n", i, arg->sizeInBytes);
-                     }
-                  } // !is_same
-               }
-            } // for each arg
-         } // if jniContext != NULL
+            fprintf(jniContext->profileFile, "%d read %s,", pos++, arg->name);
 
-         return(status);
+            fprintf(jniContext->profileFile, "%lu,%lu,%lu,%lu,",  
+                  (arg->value.ref.read.queued - currSampleBaseTime)/1000, 
+                  (arg->value.ref.read.submit - currSampleBaseTime)/1000, 
+                  (arg->value.ref.read.start - currSampleBaseTime)/1000, 
+                  (arg->value.ref.read.end - currSampleBaseTime)/1000);
+         }
       }
+   }
+   fprintf(jniContext->profileFile, "\n");
+   return(0);
+}
 
+// Should failed profiling abort the run and return early?
+cl_int profile(ProfileInfo *profileInfo, cl_event *event){
+   cl_int status = CL_SUCCESS;
+   status = clGetEventProfilingInfo(*event, CL_PROFILING_COMMAND_QUEUED, sizeof(profileInfo->queued), &(profileInfo->queued), NULL);
+   ASSERT_CL( "clGetEventProfiliningInfo() QUEUED");
+   status = clGetEventProfilingInfo(*event, CL_PROFILING_COMMAND_SUBMIT, sizeof(profileInfo->submit), &(profileInfo->submit), NULL);
+   ASSERT_CL( "clGetEventProfiliningInfo() SUBMIT");
+   status = clGetEventProfilingInfo(*event, CL_PROFILING_COMMAND_START, sizeof(profileInfo->start), &(profileInfo->start), NULL);
+   ASSERT_CL( "clGetEventProfiliningInfo() START");
+   status = clGetEventProfilingInfo(*event, CL_PROFILING_COMMAND_END, sizeof(profileInfo->end), &(profileInfo->end), NULL);
+   ASSERT_CL( "clGetEventProfiliningInfo() END");
+   return status;
+}
 
 
-      JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_runKernelJNI(JNIEnv *jenv,
-            jobject jobj, jlong jniContextHandle, jint globalSize, jint localSize, jboolean needSync,
-            jboolean useNullForLocalSize, jint passes) {
+//Step through all non-primitive (array of primitive or array object references) and determine if the field has changed
+//The field may have been re-assigned by the Java code to NULL or another instance. 
+//If we detect a change then we discard the previous cl_mem buffer, the caller will detect that the buffers are null and will create new cl_mem buffers. 
+jint updateNonPrimitiveReferences(JNIEnv *jenv, jobject jobj, JNIContext* jniContext) {
+   cl_int status = CL_SUCCESS;
+   if (jniContext != NULL){
+      for (jint i=0; i<jniContext->argc; i++){ 
+         KernelArg *arg=jniContext->args[i];
+         arg->syncType(jenv); // make sure that the JNI arg reflects the latest type info from the instance 
 
-         cl_int status = CL_SUCCESS;
-         JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
          if (jniContext->isVerbose()){
-            timer.start();
+            fprintf(stderr, "got type for %s: %08x\n", arg->name, arg->type);
          }
-
-         // Need to capture array refs
-         if (jniContext->firstRun || needSync) {
-            updateKernel(jenv, jobj, jniContext );
+         if (!arg->isPrimitive()) {
+            // Following used for all primitive arrays, object arrays and nio Buffers
+            jarray newRef = (jarray)jenv->GetObjectField(arg->javaArg, KernelArg::javaArrayFieldID);
             if (jniContext->isVerbose()){
-               fprintf(stderr, "back from updateKernel\n");
+               fprintf(stderr, "testing for Resync javaArray %s: old=%p, new=%p\n", arg->name, arg->value.ref.javaArray, newRef);         
             }
-         }
-
-         int writeEventCount = 0;
-
-         // kernelArgPos is used to keep track of the kernel arg position, it can 
-         // differ from "i" due to insertion of javaArrayLength args which are not
-         // fields read from the kernel object.
-         int kernelArgPos = 0;
-
-         for (int i=0; i< jniContext->argc; i++){
-            KernelArg *arg = jniContext->args[i];
-            // TODO: see if we can get rid of this read
-            arg->type = jenv->GetIntField(arg->javaArg, typeFieldID);
-            if (jniContext->isVerbose()){
-               fprintf(stderr, "got type for arg %d, %s, type=%08x\n", i, arg->name, arg->type);
-            }
-            if (!arg->isPrimitive() && !arg->isLocal()) {
-               // pin the arrays so that GC does not move them during the call
-
-               // get the C memory address for the region being transferred
-               // this uses different JNI calls for arrays vs. directBufs
-               void * prevAddr =  arg->value.ref.addr;
-               if (arg->value.ref.isArray) {
-                  arg->pin(jenv);
-               } else if (arg->isAparapiBufIsDirect()) {
-                  // different call used for directbuffers
-                  arg->value.ref.addr = jenv->GetDirectBufferAddress(arg->value.ref.javaArray);
-               }
 
+            if (!jenv->IsSameObject(newRef, arg->value.ref.javaArray)) {
                if (jniContext->isVerbose()){
-                  fprintf(stderr, "runKernel: arrayOrBuf ref %p, oldAddr=%p, newAddr=%p, ref.mem=%p, isArray=%d\n",
-                        arg->value.ref.javaArray, 
-                        prevAddr,
-                        arg->value.ref.addr,
-                        arg->value.ref.mem,
-                        arg->value.ref.isArray );
-                  fprintf(stderr, "at memory addr %p, contents: ", arg->value.ref.addr);
-                  unsigned char *pb = (unsigned char *) arg->value.ref.addr;
-                  for (int k=0; k<8; k++) {
-                     fprintf(stderr, "%02x ", pb[k]);
-                  }
-                  fprintf(stderr, "\n" );
+                  fprintf(stderr, "Resync javaArray for %s: %p  %p\n", arg->name, newRef, arg->value.ref.javaArray);         
                }
-               // record whether object moved 
-               // if we see that isCopy was returned by getPrimitiveArrayCritical, treat that as a move
-               bool objectMoved = (arg->value.ref.addr != prevAddr) || arg->value.ref.isCopy;
-
-#ifdef VERBOSE_EXPLICIT
-               if (arg->isExplicit() && arg->isExplicitWrite()){
-                  fprintf(stderr, "explicit write of %s\n",  arg->name);
-               }
-#endif
-
-               if (jniContext->firstRun || (arg->value.ref.mem == 0) || objectMoved ){
-                  // if either this is the first run or user changed input array
-                  // or gc moved something, then we create buffers/args
-                  cl_uint mask = CL_MEM_USE_HOST_PTR;
-                  if (arg->isRead() && arg->isWrite()) mask |= CL_MEM_READ_WRITE;
-                  else if (arg->isRead() && !arg->isWrite()) mask |= CL_MEM_READ_ONLY;
-                  else if (arg->isWrite()) mask |= CL_MEM_WRITE_ONLY;
-                  arg->value.ref.memMask = mask;
+               // Free previous ref if any
+               if (arg->value.ref.javaArray != NULL) {
+                  jenv->DeleteWeakGlobalRef((jweak) arg->value.ref.javaArray);
                   if (jniContext->isVerbose()){
-                     strcpy(arg->value.ref.memSpec,"CL_MEM_USE_HOST_PTR");
-                     if (mask & CL_MEM_READ_WRITE) strcat(arg->value.ref.memSpec,"|CL_MEM_READ_WRITE");
-                     if (mask & CL_MEM_READ_ONLY) strcat(arg->value.ref.memSpec,"|CL_MEM_READ_ONLY");
-                     if (mask & CL_MEM_WRITE_ONLY) strcat(arg->value.ref.memSpec,"|CL_MEM_WRITE_ONLY");
-
-                     fprintf(stderr, "%s %d clCreateBuffer(context, %s, size=%08x bytes, address=%08x, &status)\n", arg->name, 
-                           i, arg->value.ref.memSpec, arg->sizeInBytes, arg->value.ref.addr);
-                  }
-                  arg->value.ref.mem = clCreateBuffer(jniContext->context, arg->value.ref.memMask, 
-                        arg->sizeInBytes, arg->value.ref.addr, &status);
-
-                  if (status != CL_SUCCESS) {
-                     PRINT_CL_ERR(status, "clCreateBuffer");
-                     jniContext->unpinAll();
-                     return status;
-                  }
-
-                  status = clSetKernelArg(jniContext->kernel, kernelArgPos++, sizeof(cl_mem), (void *)&(arg->value.ref.mem));                  
-                  if (status != CL_SUCCESS) {
-                     PRINT_CL_ERR(status, "clSetKernelArg (array)");
-                     jniContext->unpinAll();
-                     return status;
-                  }
-
-                  // Add the array length if needed
-                  if (arg->usesArrayLength()){
-                     arg->value.ref.javaArrayLength = jenv->GetIntField(arg->javaArg, numElementsFieldID);
-                     status = clSetKernelArg(jniContext->kernel, kernelArgPos++, sizeof(jint), &(arg->value.ref.javaArrayLength));
-
-                     if (jniContext->isVerbose()){
-                        fprintf(stderr, "runKernel arg %d %s, javaArrayLength = %d\n", i, arg->name, arg->value.ref.javaArrayLength);
-                     }
-                     if (status != CL_SUCCESS) {
-                        PRINT_CL_ERR(status, "clSetKernelArg (array length)");
-                        jniContext->unpinAll();
-                        return status;
-                     }
-                  }
-               } else {
-                  // Keep the arg position in sync if no updates were required
-                  kernelArgPos++;
-                  if (arg->usesArrayLength()){
-                     kernelArgPos++;
+                     fprintf(stderr, "DeleteWeakGlobalRef for %s: %p\n", arg->name, arg->value.ref.javaArray);         
                   }
                }
 
-               // we only enqueue a write if we know the kernel actually reads the buffer or if there is an explicit write pending
-               // the default behavior for Constant buffers is also that there is no write enqueued unless explicit
+               // need to free opencl buffers, run will reallocate later
+               if (arg->value.ref.mem != 0) {
+                  //fprintf(stderr, "-->releaseMemObject[%d]\n", i);
+                  status = clReleaseMemObject((cl_mem)arg->value.ref.mem);
+                  //fprintf(stderr, "<--releaseMemObject[%d]\n", i);
+                  ASSERT_CL("clReleaseMemObject()");
+                  arg->value.ref.mem = (cl_mem)0;
+               }
 
-               if (arg->mustWriteBuffer()){
-#ifdef VERBOSE_EXPLICIT
-                  if (arg->isExplicit() && arg->isExplicitWrite()){
-                     fprintf(stderr, "writing explicit buffer %d %s\n", i, arg->name);
-                  }
-#endif
-                  if (jniContext->isVerbose()){
-                     fprintf(stderr, "%s writing buffer %d %s\n",  (arg->isExplicit() ? "explicitly" : ""), 
-                           i, arg->name);
-                  }
-                  if (jniContext->isProfilingEnabled()) {
-                     jniContext->writeEventArgs[writeEventCount]=i;
-                  }
+               arg->value.ref.mem = (cl_mem) 0;
+               arg->value.ref.addr = NULL;
 
-                  status = clEnqueueWriteBuffer(jniContext->commandQueues[0], arg->value.ref.mem, CL_FALSE, 0, 
-                        arg->sizeInBytes, arg->value.ref.addr, 0, NULL, &(jniContext->writeEvents[writeEventCount++]));
-                  if (status != CL_SUCCESS) {
-                     PRINT_CL_ERR(status, "clEnqueueWriteBuffer");
-                     jniContext->unpinAll();
-                     return status;
-                  }
-                  if (arg->isExplicit() && arg->isExplicitWrite()){
-                     arg->type &= ~com_amd_aparapi_KernelRunner_ARG_EXPLICIT_WRITE;
-#ifdef VERBOSE_EXPLICIT
-                     fprintf(stderr, "clearing explicit buffer bit %d %s\n", i, arg->name);
-#endif
-                     jenv->SetIntField(arg->javaArg, typeFieldID,arg->type );
-                  }
-               }
-            } else if (arg->isLocal()){
-               if (jniContext->firstRun){
-                  // must multiply perlocalByteSize by localSize to get real opencl buffer size
-                  int bytesPerLocalSize = jenv->GetIntField(arg->javaArg, bytesPerLocalSizeFieldID);
-                  int adjustedLocalBufSize = bytesPerLocalSize * localSize;
+               // Capture new array ref from the kernel arg object
 
+               if (newRef != NULL) {
+                  arg->value.ref.javaArray = (jarray)jenv->NewWeakGlobalRef((jarray)newRef);
                   if (jniContext->isVerbose()){
-                     fprintf(stderr, "ISLOCAL, clSetKernelArg(jniContext->kernel, %d, %d, NULL);\n", i, adjustedLocalBufSize);
-                  }
-                  status = clSetKernelArg(jniContext->kernel, kernelArgPos++, adjustedLocalBufSize, NULL);
-                  if (status != CL_SUCCESS) {
-                     PRINT_CL_ERR(status, "clSetKernelArg() (local)");
-                     jniContext->unpinAll();
-                     return status;
+                     fprintf(stderr, "NewWeakGlobalRef for %s, set to %p\n", arg->name,
+                           arg->value.ref.javaArray);         
                   }
                } else {
-                  // Keep the arg position in sync if no updates were required
-                  kernelArgPos++;
-                  if (arg->usesArrayLength()){
-                     kernelArgPos++;
-                  }
+                  arg->value.ref.javaArray = NULL;
                }
-            }else{  // primitive arguments
+               arg->value.ref.isArray = true;
 
-               // we need to reflectively sync the value out of the kernel object
-               if (arg->isFloat()){
-                  if (arg->isStatic){
-                     arg->value.f = jenv->GetStaticFloatField(jniContext->kernelClass, arg->fieldID);
-                  }else{
-                     arg->value.f = jenv->GetFloatField(jniContext->kernelObject, arg->fieldID);
-                  }
-                  //fprintf(stderr, "float arg %d\n", arg->value.f); 
-               }else if (arg->isInt()){
-                  if (arg->isStatic){
-                     arg->value.i = jenv->GetStaticIntField(jniContext->kernelClass, arg->fieldID);
-                  }else{
-                     arg->value.i = jenv->GetIntField(jniContext->kernelObject, arg->fieldID);
-                  }
-                  //fprintf(stderr, "int arg %d\n", arg->value.i); 
-               }else if (arg->isBoolean()){
-                  if (arg->isStatic){
-                     arg->value.c = jenv->GetStaticBooleanField(jniContext->kernelClass, arg->fieldID);
-                  }else{
-                     arg->value.c = jenv->GetBooleanField(jniContext->kernelObject, arg->fieldID);
-                  }
-                  //fprintf(stderr, "boolean arg %d\n", arg->value.c); 
-               }else if (arg->isByte()){
-                  if (arg->isStatic){
-                     arg->value.c = jenv->GetStaticByteField(jniContext->kernelClass, arg->fieldID);
-                  }else{
-                     arg->value.c = jenv->GetByteField(jniContext->kernelObject, arg->fieldID);
-                  }
-                  //fprintf(stderr, "byte arg %d\n", arg->value.c); 
-               }else if (arg->isLong()){
-                  if (arg->isStatic){
-                     arg->value.j = jenv->GetStaticLongField(jniContext->kernelClass, arg->fieldID);
-                  }else{
-                     arg->value.j = jenv->GetLongField(jniContext->kernelObject, arg->fieldID);
-                  }
-                  //fprintf(stderr, "long arg %d\n", arg->value.c); 
-               }else if (arg->isDouble()){
-                  if (arg->isStatic){
-                     arg->value.d = jenv->GetStaticDoubleField(jniContext->kernelClass, arg->fieldID);
-                  }else{
-                     arg->value.d = jenv->GetDoubleField(jniContext->kernelObject, arg->fieldID);
-                  }
-                  //fprintf(stderr, "double arg %d\n", arg->value.c); 
-               }
+               // Save the sizeInBytes which was set on the java side
+               arg->syncSizeInBytes(jenv);
 
                if (jniContext->isVerbose()){
-                  fprintf(stderr, "clSetKernelArg %s: %d %d %d 0x%08x\n", arg->name, i, kernelArgPos, 
-                        arg->sizeInBytes, arg->value);         
-               }
-               status = clSetKernelArg(jniContext->kernel, kernelArgPos++, arg->sizeInBytes, &(arg->value));
-               if (status != CL_SUCCESS) {
-                  PRINT_CL_ERR(status, "clSetKernelArg() (value)");
-                  jniContext->unpinAll();
-                  return status;
+                  fprintf(stderr, "updateNonPrimitiveReferences, args[%d].sizeInBytes=%d\n", i, arg->sizeInBytes);
                }
-            }
-         }  // for each arg
+            } // object has changed
+         }
+      } // for each arg
+   } // if jniContext != NULL
+   return(status);
+}
 
-         size_t globalSizeAsSizeT = (globalSize /jniContext->deviceIdc);
-         size_t localSizeAsSizeT = localSize;
 
-         // To support multiple passes we add a 'secret' final arg called 'passid' and just schedule multiple enqueuendrange kernels.  Each of which having a separate value of passid
 
-         for (int passid=0; passid<passes; passid++){
-            for (int dev =0; dev < jniContext->deviceIdc; dev++){
-               size_t offset = (size_t)((globalSize/jniContext->deviceIdc)*dev);
-               status = clSetKernelArg(jniContext->kernel, kernelArgPos, sizeof(passid), &(passid));
-               if (status != CL_SUCCESS) {
-                  PRINT_CL_ERR(status, "clSetKernelArg() (passid)");
-                  jniContext->unpinAll();
-                  return status;
-               }
+JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_runKernelJNI(JNIEnv *jenv,
+      jobject jobj, jlong jniContextHandle, jobject _range, jboolean needSync, jint passes) {
 
-               // four options here due to passid
-               if (passid == 0 && passes==1){
-                  //fprintf(stderr, "setting passid to %d of %d first and last\n", passid, passes);
-                  // there is one pass and this is it
-                  // enqueue depends on write enqueues 
-                  // we don't block but and we populate the executeEvents
-                  status = clEnqueueNDRangeKernel(jniContext->commandQueues[dev], jniContext->kernel, 1, &offset, &globalSizeAsSizeT,
-                        useNullForLocalSize ? NULL : &localSizeAsSizeT,
-                        writeEventCount, writeEventCount?jniContext->writeEvents:NULL, &jniContext->executeEvents[dev]);
-               }else if (passid == 0){
-                  //fprintf(stderr, "setting passid to %d of %d first not last\n", passid, passes);
-                  // this is the first of multiple passes
-                  // enqueue depends on write enqueues 
-                  // we block but do not populate executeEvents (only the last pass does this)
-                  status = clEnqueueNDRangeKernel(jniContext->commandQueues[dev], jniContext->kernel, 1, &offset, &globalSizeAsSizeT,
-                        useNullForLocalSize ? NULL : &localSizeAsSizeT,
-                        writeEventCount, writeEventCount?jniContext->writeEvents:NULL, &jniContext->executeEvents[dev]);
-
-               }else if (passid < passes-1){
-                  // we are in some middle pass (neither first or last) 
-                  // we don't depend on write enqueues
-                  // we block and do not supply executeEvents (only the last pass does this)
-                  //fprintf(stderr, "setting passid to %d of %d not first not last\n", passid, passes);
-                  status = clEnqueueNDRangeKernel(jniContext->commandQueues[dev], jniContext->kernel, 1, &offset, &globalSizeAsSizeT,
-                        useNullForLocalSize ? NULL : &localSizeAsSizeT, 0, NULL, &jniContext->executeEvents[dev]);
-               }else{
-                  // we are the last pass of >1
-                  // we don't depend on write enqueues
-                  // we block and supply executeEvents
-                  //fprintf(stderr, "setting passid to %d of %d  last\n", passid, passes);
-                  status = clEnqueueNDRangeKernel(jniContext->commandQueues[dev], jniContext->kernel, 1, &offset, &globalSizeAsSizeT,
-                        useNullForLocalSize ? NULL : &localSizeAsSizeT, 0, NULL, &jniContext->executeEvents[dev]);
-               }
+   Range range(jenv, _range);
 
+   cl_int status = CL_SUCCESS;
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext->isVerbose()){
+      timer.start();
+   }
 
-               if (status != CL_SUCCESS) {
-                  PRINT_CL_ERR(status, "clEnqueueNDRangeKernel()");
-                  fprintf(stderr, "after clEnqueueNDRangeKernel, globalSize=%d localSize=%d usingNull=%d\n", (int)globalSizeAsSizeT, (int)localSizeAsSizeT, useNullForLocalSize);
-                  jniContext->unpinAll();
-                  return status;
-               }
-            }
-            if (passid < passes-1){
-               // we need to wait for the executions to complete...
-               status = clWaitForEvents(jniContext->deviceIdc,  jniContext->executeEvents);
-               if (status != CL_SUCCESS) {
-                  PRINT_CL_ERR(status, "clWaitForEvents() execute events mid pass");
-                  jniContext->unpinAll();
-                  return status;
-               }
+   // Need to capture array refs
+   if (jniContext->firstRun || needSync) {
+      updateNonPrimitiveReferences(jenv, jobj, jniContext );
+      if (jniContext->isVerbose()){
+         fprintf(stderr, "back from updateNonPrimitiveReferences\n");
+      }
+   }
 
-               for (int dev = 0; dev < jniContext->deviceIdc; dev++){
-                  status = clReleaseEvent(jniContext->executeEvents[dev]);
-                  if (status != CL_SUCCESS) {
-                     PRINT_CL_ERR(status, "clReleaseEvent() read event");
-                     jniContext->unpinAll();
-                     return status;
-                  }
-               }
-            }
+   int writeEventCount = 0;
+
+   // kernelArgPos is used to keep track of the kernel arg position, it can 
+   // differ from "i" due to insertion of javaArrayLength args which are not
+   // fields read from the kernel object.
+   int kernelArgPos = 0;
+
+   for (int i=0; i< jniContext->argc; i++){
+      KernelArg *arg = jniContext->args[i];
+      // TODO: see if we can get rid of this read 
+      arg->syncType(jenv);
+      if (jniContext->isVerbose()){
+         fprintf(stderr, "got type for arg %d, %s, type=%08x\n", i, arg->name, arg->type);
+      }
+      if (!arg->isPrimitive() && !arg->isLocal()) {
+         // pin the arrays so that GC does not move them during the call
+
+         // get the C memory address for the region being transferred
+         // this uses different JNI calls for arrays vs. directBufs
+         void * prevAddr =  arg->value.ref.addr;
+         if (arg->value.ref.isArray) {
+            arg->pin(jenv);
          }
 
-         int readEventCount = 0;
+         if (jniContext->isVerbose()){
+            fprintf(stderr, "runKernel: arrayOrBuf ref %p, oldAddr=%p, newAddr=%p, ref.mem=%p, isArray=%d\n",
+                  arg->value.ref.javaArray, 
+                  prevAddr,
+                  arg->value.ref.addr,
+                  arg->value.ref.mem,
+                  arg->value.ref.isArray );
+            fprintf(stderr, "at memory addr %p, contents: ", arg->value.ref.addr);
+            unsigned char *pb = (unsigned char *) arg->value.ref.addr;
+            for (int k=0; k<8; k++) {
+               fprintf(stderr, "%02x ", pb[k]);
+            }
+            fprintf(stderr, "\n" );
+         }
+         // record whether object moved 
+         // if we see that isCopy was returned by getPrimitiveArrayCritical, treat that as a move
+         bool objectMoved = (arg->value.ref.addr != prevAddr) || arg->value.ref.isCopy;
 
-         for (int i=0; i< jniContext->argc; i++){
-            KernelArg *arg = jniContext->args[i];
+#ifdef VERBOSE_EXPLICIT
+         if (arg->isExplicit() && arg->isExplicitWrite()){
+            fprintf(stderr, "explicit write of %s\n",  arg->name);
+         }
+#endif
 
-            if (arg->mustReadBuffer()){
-               if (jniContext->isProfilingEnabled()) {
-                  jniContext->readEventArgs[readEventCount]=i;
-               }
-               if (jniContext->isVerbose()){
-                  fprintf(stderr, "reading buffer %d %s\n", i, arg->name);
-               }
+         if (jniContext->firstRun || (arg->value.ref.mem == 0) || objectMoved ){
+            // if either this is the first run or user changed input array
+            // or gc moved something, then we create buffers/args
+            cl_uint mask = CL_MEM_USE_HOST_PTR;
+            if (arg->isRead() && arg->isWrite()) mask |= CL_MEM_READ_WRITE;
+            else if (arg->isRead() && !arg->isWrite()) mask |= CL_MEM_READ_ONLY;
+            else if (arg->isWrite()) mask |= CL_MEM_WRITE_ONLY;
+            arg->value.ref.memMask = mask;
+            if (jniContext->isVerbose()){
+               strcpy(arg->value.ref.memSpec,"CL_MEM_USE_HOST_PTR");
+               if (mask & CL_MEM_READ_WRITE) strcat(arg->value.ref.memSpec,"|CL_MEM_READ_WRITE");
+               if (mask & CL_MEM_READ_ONLY) strcat(arg->value.ref.memSpec,"|CL_MEM_READ_ONLY");
+               if (mask & CL_MEM_WRITE_ONLY) strcat(arg->value.ref.memSpec,"|CL_MEM_WRITE_ONLY");
 
-               status = clEnqueueReadBuffer(jniContext->commandQueues[0], arg->value.ref.mem, CL_FALSE, 0, 
-                     arg->sizeInBytes,arg->value.ref.addr , jniContext->deviceIdc, jniContext->executeEvents, &(jniContext->readEvents[readEventCount++]));
-               if (status != CL_SUCCESS) {
-                  PRINT_CL_ERR(status, "clEnqueueReadBuffer()");
-                  jniContext->unpinAll();
-                  return status;
-               }
+               fprintf(stderr, "%s %d clCreateBuffer(context, %s, size=%08x bytes, address=%08x, &status)\n", arg->name, 
+                     i, arg->value.ref.memSpec, arg->sizeInBytes, arg->value.ref.addr);
             }
-         }
+            arg->value.ref.mem = clCreateBuffer(jniContext->context, arg->value.ref.memMask, 
+                  arg->sizeInBytes, arg->value.ref.addr, &status);
 
-         // don't change the order here
-         // We wait for the reads which each depend on the execution, which depends on the writes ;)
-         // So after the reads have completed, we can release the execute and writes.
+            if (status != CL_SUCCESS) {
+               PRINT_CL_ERR(status, "clCreateBuffer");
+               jniContext->unpinAll(jenv);
+               return status;
+            }
 
-         if (readEventCount >0){
-            status = clWaitForEvents(readEventCount, jniContext->readEvents);
+            status = clSetKernelArg(jniContext->kernel, kernelArgPos++, sizeof(cl_mem), (void *)&(arg->value.ref.mem));                  
             if (status != CL_SUCCESS) {
-               PRINT_CL_ERR(status, "clWaitForEvents() read events");
-               jniContext->unpinAll();
+               PRINT_CL_ERR(status, "clSetKernelArg (array)");
+               jniContext->unpinAll(jenv);
                return status;
             }
 
-            for (int i=0; i< readEventCount; i++){
-               if (jniContext->isProfilingEnabled()) {
-                  status = profile(&jniContext->args[jniContext->readEventArgs[i]]->value.ref.read, &jniContext->readEvents[i]);
-                  if (status != CL_SUCCESS) {
-                     jniContext->unpinAll();
-                     return status;
-                  }
+            // Add the array length if needed
+            if (arg->usesArrayLength()){
+               arg->syncJavaArrayLength(jenv);
+
+               status = clSetKernelArg(jniContext->kernel, kernelArgPos++, sizeof(jint), &(arg->value.ref.javaArrayLength));
+
+               if (jniContext->isVerbose()){
+                  fprintf(stderr, "runKernel arg %d %s, javaArrayLength = %d\n", i, arg->name, arg->value.ref.javaArrayLength);
                }
-               status = clReleaseEvent(jniContext->readEvents[i]);
                if (status != CL_SUCCESS) {
-                  PRINT_CL_ERR(status, "clReleaseEvent() read event");
-                  jniContext->unpinAll();
+                  PRINT_CL_ERR(status, "clSetKernelArg (array length)");
+                  jniContext->unpinAll(jenv);
                   return status;
                }
             }
          } else {
-            // if readEventCount == 0 then we don't need any reads so we just wait for the executions to complete
-            status = clWaitForEvents(jniContext->deviceIdc, jniContext->executeEvents);
+            // Keep the arg position in sync if no updates were required
+            kernelArgPos++;
+            if (arg->usesArrayLength()){
+               kernelArgPos++;
+            }
+         }
+
+         // we only enqueue a write if we know the kernel actually reads the buffer or if there is an explicit write pending
+         // the default behavior for Constant buffers is also that there is no write enqueued unless explicit
+
+         if (arg->mustWriteBuffer()){
+#ifdef VERBOSE_EXPLICIT
+            if (arg->isExplicit() && arg->isExplicitWrite()){
+               fprintf(stderr, "writing explicit buffer %d %s\n", i, arg->name);
+            }
+#endif
+            if (jniContext->isVerbose()){
+               fprintf(stderr, "%s writing buffer %d %s\n",  (arg->isExplicit() ? "explicitly" : ""), 
+                     i, arg->name);
+            }
+            if (jniContext->isProfilingEnabled()) {
+               jniContext->writeEventArgs[writeEventCount]=i;
+            }
+
+            status = clEnqueueWriteBuffer(jniContext->commandQueues[0], arg->value.ref.mem, CL_FALSE, 0, 
+                  arg->sizeInBytes, arg->value.ref.addr, 0, NULL, &(jniContext->writeEvents[writeEventCount++]));
             if (status != CL_SUCCESS) {
-               PRINT_CL_ERR(status, "clWaitForEvents() execute event");
-               jniContext->unpinAll();
+               PRINT_CL_ERR(status, "clEnqueueWriteBuffer");
+               jniContext->unpinAll(jenv);
                return status;
             }
+            if (arg->isExplicit() && arg->isExplicitWrite()){
+#ifdef VERBOSE_EXPLICIT
+               fprintf(stderr, "clearing explicit buffer bit %d %s\n", i, arg->name);
+#endif
+               arg->clearExplicitBufferBit(jenv);
+            }
          }
+      } else if (arg->isLocal()){
+         if (jniContext->firstRun){
+            int bytes = arg->sizeInBytes;
 
-         if (jniContext->isProfilingEnabled()) {
-            status = profile(&jniContext->exec, &jniContext->executeEvents[0]);
+            if (jniContext->isVerbose()){
+               fprintf(stderr, "ISLOCAL, clSetKernelArg(jniContext->kernel, %d, %d, NULL);\n", i, bytes);
+            }
+            status = clSetKernelArg(jniContext->kernel, kernelArgPos++, bytes, NULL);
             if (status != CL_SUCCESS) {
-               jniContext->unpinAll();
+               PRINT_CL_ERR(status, "clSetKernelArg() (local)");
+               jniContext->unpinAll(jenv);
                return status;
             }
+         } else {
+            // Keep the arg position in sync if no updates were required
+            kernelArgPos++;
+            if (arg->usesArrayLength()){
+               kernelArgPos++;
+            }
+         }
+      }else{  // primitive arguments
+
+         // we need to reflectively sync the value out of the kernel object
+         if (arg->isFloat()){
+            if (arg->isStatic){
+               arg->value.f = jenv->GetStaticFloatField(jniContext->kernelClass, arg->fieldID);
+            }else{
+               arg->value.f = jenv->GetFloatField(jniContext->kernelObject, arg->fieldID);
+            }
+            //fprintf(stderr, "float arg %d\n", arg->value.f); 
+         }else if (arg->isInt()){
+            if (arg->isStatic){
+               arg->value.i = jenv->GetStaticIntField(jniContext->kernelClass, arg->fieldID);
+            }else{
+               arg->value.i = jenv->GetIntField(jniContext->kernelObject, arg->fieldID);
+            }
+            //fprintf(stderr, "int arg %d\n", arg->value.i); 
+         }else if (arg->isBoolean()){
+            if (arg->isStatic){
+               arg->value.c = jenv->GetStaticBooleanField(jniContext->kernelClass, arg->fieldID);
+            }else{
+               arg->value.c = jenv->GetBooleanField(jniContext->kernelObject, arg->fieldID);
+            }
+            //fprintf(stderr, "boolean arg %d\n", arg->value.c); 
+         }else if (arg->isByte()){
+            if (arg->isStatic){
+               arg->value.c = jenv->GetStaticByteField(jniContext->kernelClass, arg->fieldID);
+            }else{
+               arg->value.c = jenv->GetByteField(jniContext->kernelObject, arg->fieldID);
+            }
+            //fprintf(stderr, "byte arg %d\n", arg->value.c); 
+         }else if (arg->isLong()){
+            if (arg->isStatic){
+               arg->value.j = jenv->GetStaticLongField(jniContext->kernelClass, arg->fieldID);
+            }else{
+               arg->value.j = jenv->GetLongField(jniContext->kernelObject, arg->fieldID);
+            }
+            //fprintf(stderr, "long arg %d\n", arg->value.c); 
+         }else if (arg->isDouble()){
+            if (arg->isStatic){
+               arg->value.d = jenv->GetStaticDoubleField(jniContext->kernelClass, arg->fieldID);
+            }else{
+               arg->value.d = jenv->GetDoubleField(jniContext->kernelObject, arg->fieldID);
+            }
+            //fprintf(stderr, "double arg %d\n", arg->value.c); 
+         }
+
+         if (jniContext->isVerbose()){
+            fprintf(stderr, "clSetKernelArg %s: %d %d %d 0x%08x\n", arg->name, i, kernelArgPos, 
+                  arg->sizeInBytes, arg->value);         
          }
+         status = clSetKernelArg(jniContext->kernel, kernelArgPos++, arg->sizeInBytes, &(arg->value));
+         if (status != CL_SUCCESS) {
+            PRINT_CL_ERR(status, "clSetKernelArg() (value)");
+            jniContext->unpinAll(jenv);
+            return status;
+         }
+      }
+   }  // for each arg
 
-         // extract the execution status from the executeEvent
-         cl_int executeStatus;
-         status = clGetEventInfo(jniContext->executeEvents[0], CL_EVENT_COMMAND_EXECUTION_STATUS, sizeof(cl_int), &executeStatus, NULL);
+   // We will need to revisit the execution of multiple devices.  
+   // POssibly cloning the range per device and mutating each to handle a unique subrange (of global) and
+   // maybe even pushing the offset into the range class.
+
+   //   size_t globalSize_0AsSizeT = (range.globalDims[0] /jniContext->deviceIdc);
+   //   size_t localSize_0AsSizeT = range.localDims[0];
+
+   // To support multiple passes we add a 'secret' final arg called 'passid' and just schedule multiple enqueuendrange kernels.  Each of which having a separate value of passid
+   //
+
+   for (int passid=0; passid<passes; passid++){
+      for (int dev =0; dev < jniContext->deviceIdc; dev++){ // this will always be 1 until we reserect multi-dim support
+         //size_t offset = 1; // (size_t)((range.globalDims[0]/jniContext->deviceIdc)*dev);
+         status = clSetKernelArg(jniContext->kernel, kernelArgPos, sizeof(passid), &(passid));
          if (status != CL_SUCCESS) {
-            PRINT_CL_ERR(status, "clGetEventInfo() execute event");
-            jniContext->unpinAll();
+            PRINT_CL_ERR(status, "clSetKernelArg() (passid)");
+            jniContext->unpinAll(jenv);
             return status;
          }
-         if (executeStatus != CL_SUCCESS) {
-            // it should definitely not be negative, but since we did a wait above, it had better be CL_COMPLETE==CL_SUCCESS
-            PRINT_CL_ERR(executeStatus, "Execution status of execute event");
-            jniContext->unpinAll();
-            return executeStatus;
+
+         // four options here due to passid
+         if (passid == 0 && passes==1){
+            //fprintf(stderr, "setting passid to %d of %d first and last\n", passid, passes);
+            // there is one pass and this is it
+            // enqueue depends on write enqueues 
+            // we don't block but and we populate the executeEvents
+            status = clEnqueueNDRangeKernel(
+                  jniContext->commandQueues[dev],
+                  jniContext->kernel,
+                  range.dims,
+                  range.offsets, range.globalDims,
+                  range.localDims,
+                  writeEventCount,
+                  writeEventCount?jniContext->writeEvents:NULL,
+                  &jniContext->executeEvents[dev]);
+         }else if (passid == 0){
+            //fprintf(stderr, "setting passid to %d of %d first not last\n", passid, passes);
+            // this is the first of multiple passes
+            // enqueue depends on write enqueues 
+            // we block but do not populate executeEvents (only the last pass does this)
+            status = clEnqueueNDRangeKernel(
+                  jniContext->commandQueues[dev],
+                  jniContext->kernel,
+                  range.dims,
+                  range.offsets,
+                  range.globalDims,
+                  range.localDims,
+                  writeEventCount,
+                  writeEventCount?jniContext->writeEvents:NULL,
+                  &jniContext->executeEvents[dev]);
+
+         }else if (passid < passes-1){
+            // we are in some middle pass (neither first or last) 
+            // we don't depend on write enqueues
+            // we block and do not supply executeEvents (only the last pass does this)
+            //fprintf(stderr, "setting passid to %d of %d not first not last\n", passid, passes);
+            status = clEnqueueNDRangeKernel(
+                  jniContext->commandQueues[dev], 
+                  jniContext->kernel,
+                  range.dims,
+                  range.offsets,
+                  range.globalDims,
+                  range.localDims,
+                  0,    // wait for this event count
+                  NULL, // list of events to wait for
+                  &jniContext->executeEvents[dev]);
+         }else{
+            // we are the last pass of >1
+            // we don't depend on write enqueues
+            // we block and supply executeEvents
+            //fprintf(stderr, "setting passid to %d of %d  last\n", passid, passes);
+            status = clEnqueueNDRangeKernel(
+                  jniContext->commandQueues[dev], 
+                  jniContext->kernel,
+                  range.dims,
+                  range.offsets, 
+                  range.globalDims,
+                  range.localDims,
+                  0,    // wait for this event count
+                  NULL, // list of events to wait for
+                  &jniContext->executeEvents[dev]);
          }
 
-         for (int dev=0; dev<jniContext->deviceIdc; dev++){
 
-            status = clReleaseEvent(jniContext->executeEvents[dev]);
-            if (status != CL_SUCCESS) {
-               PRINT_CL_ERR(status, "clReleaseEvent() execute event");
-               jniContext->unpinAll();
-               return status;
-            }
+         if (status != CL_SUCCESS) {
+            PRINT_CL_ERR(status, "clEnqueueNDRangeKernel()");
+            fprintf(stderr, "after clEnqueueNDRangeKernel, globalSize_0=%d localSize_0=%d\n", (int)range.globalDims[0], range.localDims[0] );
+            jniContext->unpinAll(jenv);
+            return status;
+         }
+      }
+      if (passid < passes-1){
+         // we need to wait for the executions to complete...
+         status = clWaitForEvents(jniContext->deviceIdc,  jniContext->executeEvents);
+         if (status != CL_SUCCESS) {
+            PRINT_CL_ERR(status, "clWaitForEvents() execute events mid pass");
+            jniContext->unpinAll(jenv);
+            return status;
          }
 
-         for (int i=0; i< writeEventCount; i++){
-            if (jniContext->isProfilingEnabled()) {
-               profile(&jniContext->args[jniContext->writeEventArgs[i]]->value.ref.write, &jniContext->writeEvents[i]);
-            }
-            status = clReleaseEvent(jniContext->writeEvents[i]);
+         for (int dev = 0; dev < jniContext->deviceIdc; dev++){
+            status = clReleaseEvent(jniContext->executeEvents[dev]);
             if (status != CL_SUCCESS) {
-               PRINT_CL_ERR(status, "clReleaseEvent() write event");
-               jniContext->unpinAll();
+               PRINT_CL_ERR(status, "clReleaseEvent() read event");
+               jniContext->unpinAll(jenv);
                return status;
             }
          }
+      }
+   }
+
+   int readEventCount = 0;
 
-         jniContext->unpinAll();
+   for (int i=0; i< jniContext->argc; i++){
+      KernelArg *arg = jniContext->args[i];
 
+      if (arg->mustReadBuffer()){
          if (jniContext->isProfilingEnabled()) {
-            writeProfileInfo(jniContext);
+            jniContext->readEventArgs[readEventCount]=i;
          }
-
-         jniContext->firstRun = false;
          if (jniContext->isVerbose()){
-            timer.end("elapsed");
+            fprintf(stderr, "reading buffer %d %s\n", i, arg->name);
          }
 
-         //fprintf(stderr, "About to return %d from exec\n", status);
-         return(status);
+         status = clEnqueueReadBuffer(jniContext->commandQueues[0], arg->value.ref.mem, CL_FALSE, 0, 
+               arg->sizeInBytes,arg->value.ref.addr , jniContext->deviceIdc, jniContext->executeEvents, &(jniContext->readEvents[readEventCount++]));
+         if (status != CL_SUCCESS) {
+            PRINT_CL_ERR(status, "clEnqueueReadBuffer()");
+            jniContext->unpinAll(jenv);
+            return status;
+         }
       }
+   }
 
+   // don't change the order here
+   // We wait for the reads which each depend on the execution, which depends on the writes ;)
+   // So after the reads have completed, we can release the execute and writes.
 
-      // we return the JNIContext from here 
-      JNIEXPORT jlong JNICALL Java_com_amd_aparapi_KernelRunner_initJNI(JNIEnv *jenv, jclass clazz, jobject kernelObject, 
-            jint flags, jint numProcessors,
-            jint maxJTPLocalSize) {
-         cl_int status = CL_SUCCESS;
-         JNIContext* jniContext = new JNIContext(jenv, kernelObject, flags, numProcessors, maxJTPLocalSize);
-         if (jniContext->isValid()){
-            return((jlong)jniContext);
-         }else{
-            return(0L);
+   if (readEventCount >0){
+      status = clWaitForEvents(readEventCount, jniContext->readEvents);
+      if (status != CL_SUCCESS) {
+         PRINT_CL_ERR(status, "clWaitForEvents() read events");
+         jniContext->unpinAll(jenv);
+         return status;
+      }
+
+      for (int i=0; i< readEventCount; i++){
+         if (jniContext->isProfilingEnabled()) {
+            status = profile(&jniContext->args[jniContext->readEventArgs[i]]->value.ref.read, &jniContext->readEvents[i]);
+            if (status != CL_SUCCESS) {
+               jniContext->unpinAll(jenv);
+               return status;
+            }
+         }
+         status = clReleaseEvent(jniContext->readEvents[i]);
+         if (status != CL_SUCCESS) {
+            PRINT_CL_ERR(status, "clReleaseEvent() read event");
+            jniContext->unpinAll(jenv);
+            return status;
          }
       }
+   } else {
+      // if readEventCount == 0 then we don't need any reads so we just wait for the executions to complete
+      status = clWaitForEvents(jniContext->deviceIdc, jniContext->executeEvents);
+      if (status != CL_SUCCESS) {
+         PRINT_CL_ERR(status, "clWaitForEvents() execute event");
+         jniContext->unpinAll(jenv);
+         return status;
+      }
+   }
 
+   if (jniContext->isProfilingEnabled()) {
+      status = profile(&jniContext->exec, &jniContext->executeEvents[0]);
+      if (status != CL_SUCCESS) {
+         jniContext->unpinAll(jenv);
+         return status;
+      }
+   }
 
-      JNIEXPORT jlong JNICALL Java_com_amd_aparapi_KernelRunner_buildProgramJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle, jstring source) {
-         JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
-         if (jniContext == NULL){
-            return 0;
-         }
+   // extract the execution status from the executeEvent
+   cl_int executeStatus;
+   status = clGetEventInfo(jniContext->executeEvents[0], CL_EVENT_COMMAND_EXECUTION_STATUS, sizeof(cl_int), &executeStatus, NULL);
+   if (status != CL_SUCCESS) {
+      PRINT_CL_ERR(status, "clGetEventInfo() execute event");
+      jniContext->unpinAll(jenv);
+      return status;
+   }
+   if (executeStatus != CL_SUCCESS) {
+      // it should definitely not be negative, but since we did a wait above, it had better be CL_COMPLETE==CL_SUCCESS
+      PRINT_CL_ERR(executeStatus, "Execution status of execute event");
+      jniContext->unpinAll(jenv);
+      return executeStatus;
+   }
 
-         cl_int status = CL_SUCCESS;
-         const char *sourceChars = jenv->GetStringUTFChars(source, NULL);
-         CHECK(sourceChars == NULL, "jenv->GetStringUTFChars() returned null" );
-
-         size_t sourceSize[] = { strlen(sourceChars) };
-         jniContext->program = clCreateProgramWithSource( jniContext->context, 1, &sourceChars, sourceSize, &status); 
-         jenv->ReleaseStringUTFChars(source, sourceChars);
-         ASSERT_CL("clCreateProgramWithSource()");
-
-         status = clBuildProgram(jniContext->program, jniContext->deviceIdc, jniContext->deviceIds, NULL, NULL, NULL);
-
-         if(status == CL_BUILD_PROGRAM_FAILURE) {
-            cl_int logStatus;
-            size_t buildLogSize = 0;
-            status = clGetProgramBuildInfo(jniContext->program, jniContext->deviceIds[0], 
-                  CL_PROGRAM_BUILD_LOG, buildLogSize, NULL, &buildLogSize);
-            ASSERT_CL("clGetProgramBuildInfo()");
-            char * buildLog = new char[buildLogSize];
-            CHECK(buildLog == NULL, "Failed to allocate host memory. (buildLog)");
-            memset(buildLog, 0, buildLogSize);
-            status = clGetProgramBuildInfo (jniContext->program, jniContext->deviceIds[0], 
-                  CL_PROGRAM_BUILD_LOG, buildLogSize, buildLog, NULL);
-            ASSERT_CL("clGetProgramBuildInfo()");
-
-            fprintf(stderr, "clBuildProgram failed");
-            fprintf(stderr, "\n************************************************\n");
-            fprintf(stderr, "%s", buildLog);
-            fprintf(stderr, "\n************************************************\n\n\n");
-            delete []buildLog;
-            return(0);
-         }
+   for (int dev=0; dev<jniContext->deviceIdc; dev++){
 
-         jniContext->kernel = clCreateKernel(jniContext->program, "run", &status);
-         ASSERT_CL("clCreateKernel()");
+      status = clReleaseEvent(jniContext->executeEvents[dev]);
+      if (status != CL_SUCCESS) {
+         PRINT_CL_ERR(status, "clReleaseEvent() execute event");
+         jniContext->unpinAll(jenv);
+         return status;
+      }
+   }
 
-         cl_command_queue_properties queue_props = 0;
-         if (jniContext->isProfilingEnabled()) {
-            queue_props |= CL_QUEUE_PROFILING_ENABLE;
-         }
+   for (int i=0; i< writeEventCount; i++){
+      if (jniContext->isProfilingEnabled()) {
+         profile(&jniContext->args[jniContext->writeEventArgs[i]]->value.ref.write, &jniContext->writeEvents[i]);
+      }
+      status = clReleaseEvent(jniContext->writeEvents[i]);
+      if (status != CL_SUCCESS) {
+         PRINT_CL_ERR(status, "clReleaseEvent() write event");
+         jniContext->unpinAll(jenv);
+         return status;
+      }
+   }
 
-         jniContext->commandQueues= new cl_command_queue[jniContext->deviceIdc];
-         for (int dev=0; dev < jniContext->deviceIdc; dev++){
-            jniContext->commandQueues[dev]=clCreateCommandQueue(jniContext->context, (cl_device_id)jniContext->deviceIds[dev],
-                  queue_props,
-                  &status);
-            ASSERT_CL("clCreateCommandQueue()");
-         }
+   jniContext->unpinAll(jenv);
 
-         if (jniContext->isProfilingEnabled()) {
-            // compute profile filename
+   if (jniContext->isProfilingEnabled()) {
+      writeProfileInfo(jniContext);
+   }
+
+   jniContext->firstRun = false;
+   if (jniContext->isVerbose()){
+      timer.end("elapsed");
+   }
+
+   //fprintf(stderr, "About to return %d from exec\n", status);
+   return(status);
+}
+
+
+// we return the JNIContext from here 
+JNIEXPORT jlong JNICALL Java_com_amd_aparapi_KernelRunner_initJNI(JNIEnv *jenv, jclass clazz, jobject kernelObject, 
+      jint flags) {
+   cl_int status = CL_SUCCESS;
+   JNIContext* jniContext = new JNIContext(jenv, kernelObject, flags);
+   if (jniContext->isValid()){
+      return((jlong)jniContext);
+   }else{
+      return(0L);
+   }
+}
+
+
+JNIEXPORT jlong JNICALL Java_com_amd_aparapi_KernelRunner_buildProgramJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle, jstring source) {
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext == NULL){
+      return 0;
+   }
+
+   cl_int status = CL_SUCCESS;
+   const char *sourceChars = jenv->GetStringUTFChars(source, NULL);
+   CHECK(sourceChars == NULL, "jenv->GetStringUTFChars() returned null" );
+
+   size_t sourceSize[] = { strlen(sourceChars) };
+   jniContext->program = clCreateProgramWithSource( jniContext->context, 1, &sourceChars, sourceSize, &status); 
+   jenv->ReleaseStringUTFChars(source, sourceChars);
+   ASSERT_CL("clCreateProgramWithSource()");
+
+   status = clBuildProgram(jniContext->program, jniContext->deviceIdc, jniContext->deviceIds, NULL, NULL, NULL);
+
+   if(status == CL_BUILD_PROGRAM_FAILURE) {
+      cl_int logStatus;
+      size_t buildLogSize = 0;
+      status = clGetProgramBuildInfo(jniContext->program, jniContext->deviceIds[0], 
+            CL_PROGRAM_BUILD_LOG, buildLogSize, NULL, &buildLogSize);
+      ASSERT_CL("clGetProgramBuildInfo()");
+      char * buildLog = new char[buildLogSize];
+      CHECK(buildLog == NULL, "Failed to allocate host memory. (buildLog)");
+      memset(buildLog, 0, buildLogSize);
+      status = clGetProgramBuildInfo (jniContext->program, jniContext->deviceIds[0], 
+            CL_PROGRAM_BUILD_LOG, buildLogSize, buildLog, NULL);
+      ASSERT_CL("clGetProgramBuildInfo()");
+
+      fprintf(stderr, "clBuildProgram failed");
+      fprintf(stderr, "\n************************************************\n");
+      fprintf(stderr, "%s", buildLog);
+      fprintf(stderr, "\n************************************************\n\n\n");
+      delete []buildLog;
+      return(0);
+   }
+
+   jniContext->kernel = clCreateKernel(jniContext->program, "run", &status);
+   ASSERT_CL("clCreateKernel()");
+
+   cl_command_queue_properties queue_props = 0;
+   if (jniContext->isProfilingEnabled()) {
+      queue_props |= CL_QUEUE_PROFILING_ENABLE;
+   }
+
+   jniContext->commandQueues= new cl_command_queue[jniContext->deviceIdc];
+   for (int dev=0; dev < jniContext->deviceIdc; dev++){
+      jniContext->commandQueues[dev]=clCreateCommandQueue(jniContext->context, (cl_device_id)jniContext->deviceIds[dev],
+            queue_props,
+            &status);
+      ASSERT_CL("clCreateCommandQueue()");
+   }
+
+   if (jniContext->isProfilingEnabled()) {
+      // compute profile filename
 #if defined (_WIN32)
-            jint pid = GetCurrentProcessId();
+      jint pid = GetCurrentProcessId();
 #else
-            pid_t pid = getpid();
+      pid_t pid = getpid();
 #endif
-            // indicate cpu or gpu
-            // timestamp
-            // kernel name
+      // indicate cpu or gpu
+      // timestamp
+      // kernel name
 
-            jclass classMethodAccess = jenv->FindClass("java/lang/Class"); 
-            jmethodID getNameID=jenv->GetMethodID(classMethodAccess,"getName","()Ljava/lang/String;");
-            jstring className = (jstring)jenv->CallObjectMethod(jniContext->kernelClass, getNameID);
-            const char *classNameChars = jenv->GetStringUTFChars(className, NULL);
+      jclass classMethodAccess = jenv->FindClass("java/lang/Class"); 
+      jmethodID getNameID=jenv->GetMethodID(classMethodAccess,"getName","()Ljava/lang/String;");
+      jstring className = (jstring)jenv->CallObjectMethod(jniContext->kernelClass, getNameID);
+      const char *classNameChars = jenv->GetStringUTFChars(className, NULL);
 
 #define TIME_STR_LEN 200
 
-            char timeStr[TIME_STR_LEN];
-            struct tm *tmp;
-            time_t t = time(NULL);
-            tmp = localtime(&t);
-            if (tmp == NULL) {
-               perror("localtime");
-            }
-            //strftime(timeStr, TIME_STR_LEN, "%F.%H%M%S", tmp);  %F seemed to cause a core dump
-            strftime(timeStr, TIME_STR_LEN, "%H%M%S", tmp);
-
-            char* fnameStr = new char[strlen(classNameChars) + strlen(timeStr) + 128];
+      char timeStr[TIME_STR_LEN];
+      struct tm *tmp;
+      time_t t = time(NULL);
+      tmp = localtime(&t);
+      if (tmp == NULL) {
+         perror("localtime");
+      }
+      //strftime(timeStr, TIME_STR_LEN, "%F.%H%M%S", tmp);  %F seemed to cause a core dump
+      strftime(timeStr, TIME_STR_LEN, "%H%M%S", tmp);
 
-            //sprintf(fnameStr, "%s.%s.%d.%llx\n", classNameChars, timeStr, pid, jniContext);
-            sprintf(fnameStr, "aparapiprof.%s.%d.%016lx", timeStr, pid, (unsigned long)jniContext);
-            jenv->ReleaseStringUTFChars(className, classNameChars);
+      char* fnameStr = new char[strlen(classNameChars) + strlen(timeStr) + 128];
 
-            FILE* profileFile = fopen(fnameStr, "w");
-            if (profileFile != NULL) {
-               jniContext->profileFile = profileFile;
-            } else {
-               jniContext->profileFile = stderr;
-               fprintf(stderr, "Could not open profile data file %s, reverting to stderr\n", fnameStr);
-            }
-            delete []fnameStr;
-         }
+      //sprintf(fnameStr, "%s.%s.%d.%llx\n", classNameChars, timeStr, pid, jniContext);
+      sprintf(fnameStr, "aparapiprof.%s.%d.%016lx", timeStr, pid, (unsigned long)jniContext);
+      jenv->ReleaseStringUTFChars(className, classNameChars);
 
-         return((jlong)jniContext);
+      FILE* profileFile = fopen(fnameStr, "w");
+      if (profileFile != NULL) {
+         jniContext->profileFile = profileFile;
+      } else {
+         jniContext->profileFile = stderr;
+         fprintf(stderr, "Could not open profile data file %s, reverting to stderr\n", fnameStr);
       }
+      delete []fnameStr;
+   }
 
+   return((jlong)jniContext);
+}
 
-      // this is called once when the arg list is first determined for this kernel
-      JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_setArgsJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle, jobjectArray argArray, jint argc) {
-         JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
-         cl_int status = CL_SUCCESS;
-         if (jniContext != NULL){      
-            jniContext->argc = argc;
-            jniContext->args = new KernelArg*[jniContext->argc];
-            jniContext->firstRun = true;
 
-            // Step through the array of KernelArg's to capture the type data for the Kernel's data members.
-            for (jint i=0; i<jniContext->argc; i++){ 
-               KernelArg* arg = jniContext->args[i] = new KernelArg;
+// this is called once when the arg list is first determined for this kernel
+JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_setArgsJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle, jobjectArray argArray, jint argc) {
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   cl_int status = CL_SUCCESS;
+   if (jniContext != NULL){      
+      jniContext->argc = argc;
+      jniContext->args = new KernelArg*[jniContext->argc];
+      jniContext->firstRun = true;
 
+      // Step through the array of KernelArg's to capture the type data for the Kernel's data members.
+      for (jint i=0; i<jniContext->argc; i++){ 
+
+
+         jobject argObj = jenv->GetObjectArrayElement(argArray, i);
+         KernelArg* arg = jniContext->args[i] = new KernelArg(jenv, argObj);
 
-               jobject argObj = jenv->GetObjectArrayElement(argArray, i);
-               if (argClazz == 0){
-                  argClazz = cacheKernelArgFields(jenv, argObj);
-               }
-               arg->javaArg = jenv->NewGlobalRef(argObj);   // save a global ref to the java Arg Object
-
-               arg->type = jenv->GetIntField(argObj, typeFieldID);
-               arg->isStatic = jenv->GetBooleanField(argObj, isStaticFieldID);
-               jstring name  = (jstring)jenv->GetObjectField(argObj, nameFieldID);
-               const char *nameChars = jenv->GetStringUTFChars(name, NULL);
-               arg->name=strdup(nameChars);
-               jenv->ReleaseStringUTFChars(name, nameChars);
 #ifdef VERBOSE_EXPLICIT
-               if (arg->isExplicit()){
-                  fprintf(stderr, "%s is explicit!\n", arg->name);
-               }
+         if (arg->isExplicit()){
+            fprintf(stderr, "%s is explicit!\n", arg->name);
+         }
 #endif
 
-               if (arg->isPrimitive()) {
-                  // for primitives, we cache the fieldID for that field in the kernel's arg object
-                  if (arg->isFloat()){
-                     if (arg->isStatic){
-                        arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "F");
-                     }else{
-                        arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "F");
-                     }
-                     arg->sizeInBytes = sizeof(jfloat);
-                  }else if (arg->isInt()){
-                     if (arg->isStatic){
-                        arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "I");
-                     }else{
-                        arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "I");
-                     }
-                     arg->sizeInBytes = sizeof(jint);
-                  }else if (arg->isByte()){
-                     if (arg->isStatic){
-                        arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "B");
-                     }else{
-                        arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "B");
-                     }
-                     arg->sizeInBytes = sizeof(jbyte);
-                  }else if (arg->isBoolean()){
-                     if (arg->isStatic){
-                        arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "Z");
-                     }else{
-                        arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "Z");
-                     }
-                     arg->sizeInBytes = sizeof(jboolean);
-                  }else if (arg->isLong()){
-                     if (arg->isStatic){
-                        arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "J");
-                     }else{
-                        arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "J");
-                     }
-                     arg->sizeInBytes = sizeof(jlong);
-                  }else if (arg->isDouble()){
-                     if (arg->isStatic){
-                        arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "D");
-                     }else{
-                        arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "D");
-                     }
-                     arg->sizeInBytes = sizeof(jdouble);
-                  }
-               }else{ // we will use an array
-                  arg->value.ref.mem = (cl_mem) 0;
-                  arg->value.ref.javaArray = 0;
-                  arg->sizeInBytes = 0;
+         if (arg->isPrimitive()) {
+            // for primitives, we cache the fieldID for that field in the kernel's arg object
+            if (arg->isFloat()){
+               if (arg->isStatic){
+                  arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "F");
+               }else{
+                  arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "F");
                }
-               if (jniContext->isVerbose()){
-                  fprintf(stderr, "in setArgs arg %d %s type %08x\n", i, arg->name, arg->type);
+               arg->sizeInBytes = sizeof(jfloat);
+            }else if (arg->isInt()){
+               if (arg->isStatic){
+                  arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "I");
+               }else{
+                  arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "I");
                }
-
-               //If an error occurred, return early so we report the first problem, not the last
-               if (jniContext->jenv->ExceptionCheck() == JNI_TRUE) {
-                  jniContext->argc = -1;
-                  delete[] jniContext->args;
-                  jniContext->args = NULL;
-                  jniContext->firstRun = true;
-                  return (status);
+               arg->sizeInBytes = sizeof(jint);
+            }else if (arg->isByte()){
+               if (arg->isStatic){
+                  arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "B");
+               }else{
+                  arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "B");
                }
-
-            }
-            // we will need an executeEvent buffer for all devices
-            jniContext->executeEvents = new cl_event[jniContext->deviceIdc];
-
-            // We will need *at most* jniContext->argc read/write events
-            jniContext->readEvents = new cl_event[jniContext->argc];
-            if (jniContext->isProfilingEnabled()) {
-               jniContext->readEventArgs = new jint[jniContext->argc];
-            }
-            jniContext->writeEvents = new cl_event[jniContext->argc];
-            if (jniContext->isProfilingEnabled()) {
-               jniContext->writeEventArgs = new jint[jniContext->argc];
+               arg->sizeInBytes = sizeof(jbyte);
+            }else if (arg->isBoolean()){
+               if (arg->isStatic){
+                  arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "Z");
+               }else{
+                  arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "Z");
+               }
+               arg->sizeInBytes = sizeof(jboolean);
+            }else if (arg->isLong()){
+               if (arg->isStatic){
+                  arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "J");
+               }else{
+                  arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "J");
+               }
+               arg->sizeInBytes = sizeof(jlong);
+            }else if (arg->isDouble()){
+               if (arg->isStatic){
+                  arg->fieldID = jenv->GetStaticFieldID(jniContext->kernelClass, arg->name, "D");
+               }else{
+                  arg->fieldID = jenv->GetFieldID(jniContext->kernelClass, arg->name, "D");
+               }
+               arg->sizeInBytes = sizeof(jdouble);
             }
+         }else{ // we will use an array
+            arg->value.ref.mem = (cl_mem) 0;
+            arg->value.ref.javaArray = 0;
+            arg->sizeInBytes = 0;
          }
-         return(status);
-      }
-
-      JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_getLocalSizeJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle, jint globalSize, jint localBytesPerLocalId) {
-         size_t kernelMaxWorkGroupSize = 0;
-         size_t kernelWorkGroupSizeMultiple = 0;
-         cl_int status = CL_SUCCESS;
-         JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
-         if (jniContext != NULL){
-            clGetKernelWorkGroupInfo(jniContext->kernel, jniContext->deviceIds[0], CL_KERNEL_WORK_GROUP_SIZE, sizeof(kernelMaxWorkGroupSize), &kernelMaxWorkGroupSize, NULL);
-            ASSERT_CL("clGetKernelWorkGroupInfo()");
-            // starting value depends on device type
-            // not sure why the CPU has a different starting size, but it does
-            int startLocalSize = (jniContext->deviceType == CL_DEVICE_TYPE_GPU ? kernelMaxWorkGroupSize : globalSize/(jniContext->numProcessors*4));
-
-            if (startLocalSize == 0) startLocalSize = 1;
-            if (startLocalSize > kernelMaxWorkGroupSize) startLocalSize = kernelMaxWorkGroupSize;
-            if (startLocalSize > globalSize) startLocalSize = globalSize;
-            // if the kernel uses any local memory, determine our max local memory size so we can possibly limit localsize
-            cl_ulong devLocalMemSize;
-            if (localBytesPerLocalId > 0) {
-               status = clGetDeviceInfo(jniContext->deviceIds[0], CL_DEVICE_LOCAL_MEM_SIZE, sizeof(cl_ulong), &devLocalMemSize, NULL);
-               cl_uint localSizeLimitFromLocalMem = devLocalMemSize/localBytesPerLocalId;
-               if (startLocalSize > localSizeLimitFromLocalMem) startLocalSize = localSizeLimitFromLocalMem;
-               if (jniContext->isVerbose()){
-                  fprintf(stderr, "localBytesPerLocalId=%d, device localMemMax=%d, localSizeLimitFromLocalMem=%d\n",
-                        localBytesPerLocalId, (cl_uint) devLocalMemSize, localSizeLimitFromLocalMem);
-               }
-
+         if (jniContext->isVerbose()){
+            fprintf(stderr, "in setArgs arg %d %s type %08x\n", i, arg->name, arg->type);
+            if (arg->isLocal()){
+                 fprintf(stderr, "in setArgs arg %d %s is local\n", i, arg->name);
+            }else{
+                 fprintf(stderr, "in setArgs arg %d %s is *not* local\n", i, arg->name);
             }
+         }
 
-            // then iterate down until we find a localSize that divides globalSize equally
-            for (int localSize = startLocalSize; localSize>0; localSize--) {
-               if (globalSize % localSize == 0) {
-                  if (jniContext->isVerbose()){
-                     fprintf(stderr, "for globalSize=%d, stepping localSize from %d, returning localSize=%d\n", globalSize, startLocalSize, localSize);
-                  }
-                  return localSize;
-               }
-            }
+         //If an error occurred, return early so we report the first problem, not the last
+         if (jenv->ExceptionCheck() == JNI_TRUE) {
+            jniContext->argc = -1;
+            delete[] jniContext->args;
+            jniContext->args = NULL;
+            jniContext->firstRun = true;
+            return (status);
          }
-         // should never get this far
-         return 0;
+
       }
+      // we will need an executeEvent buffer for all devices
+      jniContext->executeEvents = new cl_event[jniContext->deviceIdc];
+
+      // We will need *at most* jniContext->argc read/write events
+      jniContext->readEvents = new cl_event[jniContext->argc];
+      if (jniContext->isProfilingEnabled()) {
+         jniContext->readEventArgs = new jint[jniContext->argc];
+      }
+      jniContext->writeEvents = new cl_event[jniContext->argc];
+      if (jniContext->isProfilingEnabled()) {
+         jniContext->writeEventArgs = new jint[jniContext->argc];
+      }
+   }
+   return(status);
+}
 
-      JNIEXPORT jstring JNICALL Java_com_amd_aparapi_KernelRunner_getExtensions(JNIEnv *jenv, jobject jobj, jlong jniContextHandle) {
-         jstring jextensions = NULL;
-         JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
-         if (jniContext != NULL){
-            size_t retvalsize = 0;
-            cl_int status = CL_SUCCESS;
-            status = clGetDeviceInfo(jniContext->deviceIds[0], CL_DEVICE_EXTENSIONS, 0, NULL, &retvalsize);
-            ASSERT_CL("clGetDeviceInfo()");
-            char* extensions = new char[retvalsize];
-            clGetDeviceInfo(jniContext->deviceIds[0], CL_DEVICE_EXTENSIONS, retvalsize, extensions, NULL);
-            jextensions = jenv->NewStringUTF(extensions);
-            delete [] extensions;
-         }
-         return jextensions;
-      }
-
-      // Called as a result of Kernel.get(someArray)
-      JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_getJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle, jobject buffer) {
-         cl_int status = CL_SUCCESS;
-         JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
-         if (jniContext != NULL){
-            jboolean foundArg = false;
-            for (jint i=0; i<jniContext->argc; i++){ 
-               KernelArg *arg= jniContext->args[i];
-               if (arg->isArray()){
-                  jboolean isSame = jenv->IsSameObject(buffer, arg->value.ref.javaArray);
-                  // only do this if the array that we are passed is indeed an arg we are tracking
-                  if (isSame){
-                     foundArg = true;
-                     //fprintf(stderr, "get of %s\n", arg->name);
+
+
+JNIEXPORT jstring JNICALL Java_com_amd_aparapi_KernelRunner_getExtensionsJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle) {
+   jstring jextensions = NULL;
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext != NULL){
+      size_t retvalsize = 0;
+      cl_int status = CL_SUCCESS;
+      status = clGetDeviceInfo(jniContext->deviceIds[0], CL_DEVICE_EXTENSIONS, 0, NULL, &retvalsize);
+      ASSERT_CL("clGetDeviceInfo()");
+      char* extensions = new char[retvalsize];
+      clGetDeviceInfo(jniContext->deviceIds[0], CL_DEVICE_EXTENSIONS, retvalsize, extensions, NULL);
+      jextensions = jenv->NewStringUTF(extensions);
+      delete [] extensions;
+   }
+   return jextensions;
+}
+
+// Called as a result of Kernel.get(someArray)
+JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_getJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle, jobject buffer) {
+   cl_int status = CL_SUCCESS;
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext != NULL){
+      jboolean foundArg = false;
+      for (jint i=0; i<jniContext->argc; i++){ 
+         KernelArg *arg= jniContext->args[i];
+         if (arg->isArray()){
+            jboolean isSame = jenv->IsSameObject(buffer, arg->value.ref.javaArray);
+            // only do this if the array that we are passed is indeed an arg we are tracking
+            if (isSame){
+               foundArg = true;
+               //fprintf(stderr, "get of %s\n", arg->name);
 
 #ifdef VERBOSE_EXPLICIT
-                     fprintf(stderr, "explicitly reading buffer %d %s\n", i, arg->name);
+               fprintf(stderr, "explicitly reading buffer %d %s\n", i, arg->name);
 #endif
-                     arg->pin(jenv);
+               arg->pin(jenv);
 
-                     status = clEnqueueReadBuffer(jniContext->commandQueues[0], arg->value.ref.mem, CL_FALSE, 0, 
-                           arg->sizeInBytes,arg->value.ref.addr , 0, NULL, &jniContext->readEvents[0]);
-                     if (status != CL_SUCCESS) {
-                        PRINT_CL_ERR(status, "clEnqueueReadBuffer()");
-                        return status;
-                     }
-                     status = clWaitForEvents(1, jniContext->readEvents);
-                     if (status != CL_SUCCESS) {
-                        PRINT_CL_ERR(status, "clWaitForEvents");
-                        return status;
-                     }
-                     clReleaseEvent(jniContext->readEvents[0]);
-                     if (status != CL_SUCCESS) {
-                        PRINT_CL_ERR(status, "clReleaseEvent() read event");
-                        return status;
-                     }
-                     // since this is an explicit buffer get, we expect the buffer to have changed so we commit
-                     arg->unpinCommit(jenv);
-                  }
+               status = clEnqueueReadBuffer(jniContext->commandQueues[0], arg->value.ref.mem, CL_FALSE, 0, 
+                     arg->sizeInBytes,arg->value.ref.addr , 0, NULL, &jniContext->readEvents[0]);
+               if (status != CL_SUCCESS) {
+                  PRINT_CL_ERR(status, "clEnqueueReadBuffer()");
+                  return status;
                }
-            }
-            if (!foundArg){
-               if (jniContext->isVerbose()){
-                  fprintf(stderr, "attempt to request to get a buffer that does not appear to be referenced from kernel\n");
+               status = clWaitForEvents(1, jniContext->readEvents);
+               if (status != CL_SUCCESS) {
+                  PRINT_CL_ERR(status, "clWaitForEvents");
+                  return status;
                }
+               clReleaseEvent(jniContext->readEvents[0]);
+               if (status != CL_SUCCESS) {
+                  PRINT_CL_ERR(status, "clReleaseEvent() read event");
+                  return status;
+               }
+               // since this is an explicit buffer get, we expect the buffer to have changed so we commit
+               arg->unpinCommit(jenv);
             }
          }
-         return 0;
       }
+      if (!foundArg){
+         if (jniContext->isVerbose()){
+            fprintf(stderr, "attempt to request to get a buffer that does not appear to be referenced from kernel\n");
+         }
+      }
+   }
+   return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_getMaxComputeUnitsJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle) {
+   cl_int status = CL_SUCCESS;
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext != NULL){
+      return(jniContext->maxComputeUnits);
+   }else{
+      return(0);
+   }
+}
 
+JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_getMaxWorkItemDimensionsJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle) {
+   cl_int status = CL_SUCCESS;
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext != NULL){
+      return(jniContext->maxWorkItemDimensions);
+   }else{
+      return(0);
+   }
+}
 
+JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_getMaxWorkGroupSizeJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle) {
+   cl_int status = CL_SUCCESS;
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext != NULL){
+      return(jniContext->maxWorkGroupSize);
+   }else{
+      return(0);
+   }
+}
+
+JNIEXPORT jint JNICALL Java_com_amd_aparapi_KernelRunner_getMaxWorkItemSizeJNI(JNIEnv *jenv, jobject jobj, jlong jniContextHandle, jint _index) {
+   cl_int status = CL_SUCCESS;
+   JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+   if (jniContext != NULL && _index >=0 && _index <= jniContext->maxWorkItemDimensions){
+      return(jniContext->maxWorkItemSizes[_index]);
+   }else{
+      return(0);
+   }
+}
diff --git a/com.amd.aparapi/build.xml b/com.amd.aparapi/build.xml
index c57f0cb3..099eee30 100644
--- a/com.amd.aparapi/build.xml
+++ b/com.amd.aparapi/build.xml
@@ -8,7 +8,7 @@
       <delete file="aparapi.jar"/>
    </target>
 
-   <target name="build">
+   <target name="build" depends="clean">
       <mkdir dir="classes"/>
       <javac destdir="classes" debug="on" includeAntRuntime="false" >
          <src path="src/java"/>
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/ClassModel.java b/com.amd.aparapi/src/java/com/amd/aparapi/ClassModel.java
index 57f25935..8d6b45b4 100644
--- a/com.amd.aparapi/src/java/com/amd/aparapi/ClassModel.java
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/ClassModel.java
@@ -2090,9 +2090,9 @@ class ClassModel{
       }
 
       public boolean isStatic() {
-    	  return (Access.STATIC.bitIsSet(methodAccessFlags));
+         return (Access.STATIC.bitIsSet(methodAccessFlags));
       }
-      
+
       AttributePool getAttributePool() {
          return (methodAttributePool);
       }
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/Config.java b/com.amd.aparapi/src/java/com/amd/aparapi/Config.java
index 46d2266a..192edeac 100644
--- a/com.amd.aparapi/src/java/com/amd/aparapi/Config.java
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/Config.java
@@ -175,6 +175,7 @@ class Config{
    static String instructionListenerClassName = System.getProperty(propPkgName + ".instructionListenerClass");
 
    static public InstructionListener instructionListener = null;
+
    {
 
       if (instructionListenerClassName != null && !instructionListenerClassName.equals("")) {
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/DeprecatedException.java b/com.amd.aparapi/src/java/com/amd/aparapi/DeprecatedException.java
new file mode 100644
index 00000000..70ca856e
--- /dev/null
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/DeprecatedException.java
@@ -0,0 +1,46 @@
+/*
+Copyright (c) 2010-2011, Advanced Micro Devices, Inc.
+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 the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+If you use the software (in whole or in part), you shall adhere to all applicable U.S., European, and other export
+laws, including but not limited to the U.S. Export Administration Regulations ("EAR"), (15 C.F.R. Sections 730 through
+774), and E.U. Council Regulation (EC) No 1334/2000 of 22 June 2000.  Further, pursuant to Section 740.6 of the EAR,
+you hereby certify that, except pursuant to a license granted by the United States Department of Commerce Bureau of 
+Industry and Security or as otherwise permitted pursuant to a License Exception under the U.S. Export Administration 
+Regulations ("EAR"), you will not (1) export, re-export or release to a national of a country in Country Groups D:1,
+E:1 or E:2 any restricted technology, software, or source code you receive hereunder, or (2) export to Country Groups
+D:1, E:1 or E:2 the direct product of such technology or software, if such foreign produced direct product is subject
+to national security controls as identified on the Commerce Control List (currently found in Supplement 1 to Part 774
+of EAR).  For the most current Country Group listings, or for additional information about the EAR or your obligations
+under those regulations, please refer to the U.S. Bureau of Industry and Security's website at http://www.bis.doc.gov/. 
+
+*/
+package com.amd.aparapi;
+
+@SuppressWarnings("serial") class DeprecatedException extends AparapiException{
+
+   DeprecatedException(String msg) {
+      super(msg);
+   }
+
+}
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/Kernel.java b/com.amd.aparapi/src/java/com/amd/aparapi/Kernel.java
index c6dd14a1..abef24b2 100644
--- a/com.amd.aparapi/src/java/com/amd/aparapi/Kernel.java
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/Kernel.java
@@ -89,16 +89,17 @@ import com.amd.aparapi.ClassModel.ConstantPool.MethodReferenceEntry;
  *     }
  * </pre></blockquote>
  * <p>
- * To execute this kernel, first create a new instance of it and then call <code>execute(int globalSize)</code>. 
+ * To execute this kernel, first create a new instance of it and then call <code>execute(Range _range)</code>. 
  * <p>
  * <blockquote><pre>
  *     int[] values = new int[1024];
  *     // fill values array
+ *     Range range = Range.create(values.length); // create a range 0..1024
  *     SquareKernel kernel = new SquareKernel(values);
- *     kernel.execute(values.length);
+ *     kernel.execute(range);
  * </pre></blockquote>
  * <p>
- * When <code>execute()</code> returns, all the executions of Kernel.run() have completed and the results are available in the <code>squares</code> array.
+ * When <code>execute(Range)</code> returns, all the executions of <code>Kernel.run()</code> have completed and the results are available in the <code>squares</code> array.
  * <p>
  * <blockquote><pre>
  *     int[] squares = kernel.getSquares();
@@ -110,16 +111,19 @@ import com.amd.aparapi.ClassModel.ConstantPool.MethodReferenceEntry;
  * A different approach to creating kernels that avoids extending Kernel is to write an anonymous inner class:
  * <p>
  * <blockquote><pre>
+ *   
  *     final int[] values = new int[1024];
- *     // fill values array
+ *     // fill the values array 
  *     final int[] squares = new int[values.length];
+ *     final Range range = Range.create(values.length);
+ *   
  *     Kernel kernel = new Kernel(){
  *         public void run() {
  *             int gid = getGlobalID();
  *             squares[gid] = values[gid]*values[gid];
  *         }
  *     };
- *     kernel.execute(values.length);
+ *     kernel.execute(range);
  *     for (int i=0; i< values.length; i++){
  *        System.out.printf("%4d %4d %8d\n", i, values[i], squares[i]);
  *     }
@@ -141,15 +145,52 @@ public abstract class Kernel implements Cloneable{
    }
 
    @Retention(RetentionPolicy.RUNTIME) @interface OpenCLDelegate {
+
    }
 
+   /**
+    *  We can use this Annotation to 'tag' intended local buffers. 
+    *  
+    *  So we can either annotate the buffer
+    *  <pre><code>
+    *  &#64Local int[] buffer = new int[1024];
+    *  </code></pre>
+    *   Or use a special suffix 
+    *  <pre><code>
+    *  int[] buffer_$local$ = new int[1024];
+    *  </code></pre>
+    *  
+    *  @see LOCAL_SUFFIX
+    * 
+    * 
+    */
+   public @Retention(RetentionPolicy.RUNTIME) @interface Local {
+
+   }
+
+   /**
+    *  We can use this suffix to 'tag' intended local buffers. 
+    *  
+    *  
+    *  So either name the buffer 
+    *  <pre><code>
+    *  int[] buffer_$local$ = new int[1024];
+    *  </code></pre>
+    *  Or use the Annotation form 
+    *  <pre><code>
+    *  &#64Local int[] buffer = new int[1024];
+    *  </code></pre>
+    */
+
+   final static String LOCAL_SUFFIX = "_$local$";
+
    private static Logger logger = Logger.getLogger(Config.getLoggerName());
 
    public abstract class Entry{
       public abstract void run();
 
-      public Kernel execute(int _globalSize) {
-         return (Kernel.this.execute("foo", _globalSize, 1));
+      public Kernel execute(Range _range) {
+         return (Kernel.this.execute("foo", _range, 1));
       }
    }
 
@@ -292,26 +333,30 @@ public abstract class Kernel implements Cloneable{
 
    private EXECUTION_MODE executionMode = EXECUTION_MODE.getDefaultExecutionMode();
 
-   private int globalId;
-
-   private int localId;
-
-   private int localSize;
+   int[] globalId = new int[] {
+         0,
+         0,
+         0
+   };
 
-   private int globalSize;
+   int[] localId = new int[] {
+         0,
+         0,
+         0
+   };
 
-   private int groupId;
+   int[] groupId = new int[] {
+         0,
+         0,
+         0
+   };
 
-   private int passId;
+   Range range;
 
-   private int numGroups;
+   int passId;
 
    volatile CyclicBarrier localBarrier;
 
-   void setGlobalId(int _globalId) {
-      globalId = _globalId;
-   }
-
    /**
     * Determine the globalId of an executing kernel.
     * <p>
@@ -349,19 +394,26 @@ public abstract class Kernel implements Cloneable{
     */
 
    @OpenCLDelegate protected final int getGlobalId() {
-      return (globalId);
+      return (getGlobalId(0));
    }
 
-   void setGroupId(int _groupId) {
-      groupId = _groupId;
-
+   @OpenCLDelegate protected final int getGlobalId(int _dim) {
+      return (globalId[_dim]);
    }
 
-   void setPassId(int _passId) {
-      passId = _passId;
+   /*
+      @OpenCLDelegate protected final int getGlobalX() {
+         return (getGlobalId(0));
+      }
 
-   }
+      @OpenCLDelegate protected final int getGlobalY() {
+         return (getGlobalId(1));
+      }
 
+      @OpenCLDelegate protected final int getGlobalZ() {
+         return (getGlobalId(2));
+      }
+   */
    /**
     * Determine the groupId of an executing kernel.
     * <p>
@@ -394,9 +446,26 @@ public abstract class Kernel implements Cloneable{
     * @return The groupId for this Kernel being executed
     */
    @OpenCLDelegate protected final int getGroupId() {
-      return (groupId);
+      return (getGroupId(0));
    }
 
+   @OpenCLDelegate protected final int getGroupId(int _dim) {
+      return (groupId[_dim]);
+   }
+
+   /*
+      @OpenCLDelegate protected final int getGroupX() {
+         return (getGroupId(0));
+      }
+
+      @OpenCLDelegate protected final int getGroupY() {
+         return (getGroupId(1));
+      }
+
+      @OpenCLDelegate protected final int getGroupZ() {
+         return (getGroupId(2));
+      }
+   */
    /**
     * Determine the passId of an executing kernel.
     * <p>
@@ -416,10 +485,6 @@ public abstract class Kernel implements Cloneable{
       return (passId);
    }
 
-   void setLocalId(int _localId) {
-      localId = _localId;
-   }
-
    /**
     * Determine the local id of an executing kernel.
     * <p>
@@ -451,9 +516,26 @@ public abstract class Kernel implements Cloneable{
     * @return The local id for this Kernel being executed
     */
    @OpenCLDelegate protected final int getLocalId() {
-      return (localId);
+      return (getLocalId(0));
    }
 
+   @OpenCLDelegate protected final int getLocalId(int _dim) {
+      return (localId[_dim]);
+   }
+
+   /*
+      @OpenCLDelegate protected final int getLocalX() {
+         return (getLocalId(0));
+      }
+
+      @OpenCLDelegate protected final int getLocalY() {
+         return (getLocalId(1));
+      }
+
+      @OpenCLDelegate protected final int getLocalZ() {
+         return (getLocalId(2));
+      }
+   */
    /**
     * Determine the size of the group that an executing kernel is a member of.
     * <p>
@@ -472,9 +554,26 @@ public abstract class Kernel implements Cloneable{
     * @return The size of the currently executing group.
     */
    @OpenCLDelegate protected final int getLocalSize() {
-      return (localSize);
+      return (range.getLocalSize(0));
+   }
+
+   @OpenCLDelegate protected final int getLocalSize(int _dim) {
+      return (range.getLocalSize(_dim));
    }
 
+   /*
+      @OpenCLDelegate protected final int getLocalWidth() {
+         return (range.getLocalSize(0));
+      }
+
+      @OpenCLDelegate protected final int getLocalHeight() {
+         return (range.getLocalSize(1));
+      }
+
+      @OpenCLDelegate protected final int getLocalDepth() {
+         return (range.getLocalSize(2));
+      }
+   */
    /**
     * Determine the value that was passed to <code>Kernel.execute(int globalSize)</code> method.
     * 
@@ -486,14 +585,26 @@ public abstract class Kernel implements Cloneable{
     * @return The value passed to <code>Kernel.execute(int globalSize)</code> causing the current execution.
     */
    @OpenCLDelegate protected final int getGlobalSize() {
-      return (globalSize);
+      return (range.getGlobalSize(0));
    }
 
-   void setNumGroups(int _numGroups) {
-      numGroups = _numGroups;
-
+   @OpenCLDelegate protected final int getGlobalSize(int _dim) {
+      return (range.getGlobalSize(_dim));
    }
 
+   /*
+      @OpenCLDelegate protected final int getGlobalWidth() {
+         return (range.getGlobalSize(0));
+      }
+
+      @OpenCLDelegate protected final int getGlobalHeight() {
+         return (range.getGlobalSize(1));
+      }
+
+      @OpenCLDelegate protected final int getGlobalDepth() {
+         return (range.getGlobalSize(2));
+      }
+   */
    /**
     * Determine the number of groups that will be used to execute a kernel
     * <p>
@@ -509,9 +620,26 @@ public abstract class Kernel implements Cloneable{
     * @return The number of groups that kernels will be dispatched into.
     */
    @OpenCLDelegate protected final int getNumGroups() {
-      return (numGroups);
+      return (range.getNumGroups(0));
+   }
+
+   @OpenCLDelegate protected final int getNumGroups(int _dim) {
+      return (range.getNumGroups(_dim));
    }
 
+   /*
+      @OpenCLDelegate protected final int getNumGroupsWidth() {
+         return (range.getGroups(0));
+      }
+
+      @OpenCLDelegate protected final int getNumGroupsHeight() {
+         return (range.getGroups(1));
+      }
+
+      @OpenCLDelegate protected final int getNumGroupsDepth() {
+         return (range.getGroups(2));
+      }
+   */
    /**
     * The entry point of a kernel. 
     *  
@@ -529,9 +657,21 @@ public abstract class Kernel implements Cloneable{
    @Override protected Object clone() {
       try {
          Kernel worker = (Kernel) super.clone();
-         // if there are any private buffers, go thru the fields here
-         // and allocate a new instance for each clone
-
+         worker.groupId = new int[] {
+               0,
+               0,
+               0
+         };
+         worker.localId = new int[] {
+               0,
+               0,
+               0
+         };
+         worker.globalId = new int[] {
+               0,
+               0,
+               0
+         };
          return worker;
       } catch (CloneNotSupportedException e) {
          // TODO Auto-generated catch block
@@ -1373,23 +1513,12 @@ public abstract class Kernel implements Cloneable{
     * Java version is identical to localBarrier()
     * 
     * @annotion Experimental
+    * @deprecated
     */
 
-   @OpenCLDelegate @Annotations.Experimental protected final void globalBarrier() {
-      try {
-         localBarrier.await();
-      } catch (InterruptedException e) {
-         // TODO Auto-generated catch block
-         e.printStackTrace();
-      } catch (BrokenBarrierException e) {
-         // TODO Auto-generated catch block
-         e.printStackTrace();
-      }
-   }
-
-   final void setSizes(int _globalSize, int _localSize) {
-      localSize = _localSize;
-      globalSize = _globalSize;
+   @OpenCLDelegate @Annotations.Experimental @Deprecated() protected final void globalBarrier() throws DeprecatedException {
+      throw new DeprecatedException(
+            "Kernel.globalBarrier() has been deprecated. It was based an incorrect understanding of OpenCL functionality.");
 
    }
 
@@ -1441,23 +1570,38 @@ public abstract class Kernel implements Cloneable{
    }
 
    /**
-    * Start execution of <code>globalSize</code> kernels.
+    * Start execution of <code>_range</code> kernels.
     * <p>
     * When <code>kernel.execute(globalSize)</code> is invoked, Aparapi will schedule the execution of <code>globalSize</code> kernels. If the execution mode is GPU then 
     * the kernels will execute as OpenCL code on the GPU device. Otherwise, if the mode is JTP, the kernels will execute as a pool of Java threads on the CPU. 
     * <p>
-    * @param _globalSize The number of Kernels that we would like to initiate.
+    * @param range The number of Kernels that we would like to initiate.
+    * @returnThe Kernel instance (this) so we can chain calls to put(arr).execute(range).get(arr)
+    * 
+    */
+   public synchronized Kernel execute(Range _range) {
+      return (execute(_range, 1));
+   }
+
+   /**
+    * Start execution of <code>_range</code> kernels.
+    * <p>
+    * When <code>kernel.execute(_range)</code> is invoked, Aparapi will schedule the execution of <code>_range</code> kernels. If the execution mode is GPU then 
+    * the kernels will execute as OpenCL code on the GPU device. Otherwise, if the mode is JTP, the kernels will execute as a pool of Java threads on the CPU. 
+    * <p>
+    * Since adding the new <code>Range class</code> this method offers backward compatibility and merely defers to <code> return (execute(Range.create(_range), 1));</code>.
+    * @param _range The number of Kernels that we would like to initiate.
     * @returnThe Kernel instance (this) so we can chain calls to put(arr).execute(range).get(arr)
     * 
     */
-   public synchronized Kernel execute(int _globalSize) {
-      return (execute(_globalSize, 1));
+   public synchronized Kernel execute(int _range) {
+      return (execute(Range.create(_range), 1));
    }
 
    /**
-    * Start execution of <code>_passes</code> iterations of <code>globalSize</code> kernels.
+    * Start execution of <code>_passes</code> iterations of <code>_range</code> kernels.
     * <p>
-    * When <code>kernel.execute(globalSize, passes)</code> is invoked, Aparapi will schedule the execution of <code>globalSize</code> kernels. If the execution mode is GPU then 
+    * When <code>kernel.execute(_range, _passes)</code> is invoked, Aparapi will schedule the execution of <code>_reange</code> kernels. If the execution mode is GPU then 
     * the kernels will execute as OpenCL code on the GPU device. Otherwise, if the mode is JTP, the kernels will execute as a pool of Java threads on the CPU. 
     * <p>
     * @param _globalSize The number of Kernels that we would like to initiate.
@@ -1465,8 +1609,23 @@ public abstract class Kernel implements Cloneable{
     * @return The Kernel instance (this) so we can chain calls to put(arr).execute(range).get(arr)
     * 
     */
-   public synchronized Kernel execute(int _globalSize, int _passes) {
-      return (execute("run", _globalSize, _passes));
+   public synchronized Kernel execute(Range _range, int _passes) {
+      return (execute("run", _range, _passes));
+   }
+
+   /**
+    * Start execution of <code>_passes</code> iterations over the <code>_range</code> of kernels.
+    * <p>
+    * When <code>kernel.execute(_range)</code> is invoked, Aparapi will schedule the execution of <code>_range</code> kernels. If the execution mode is GPU then 
+    * the kernels will execute as OpenCL code on the GPU device. Otherwise, if the mode is JTP, the kernels will execute as a pool of Java threads on the CPU. 
+    * <p>
+    * Since adding the new <code>Range class</code> this method offers backward compatibility and merely defers to <code> return (execute(Range.create(_range), 1));</code>.
+    * @param _range The number of Kernels that we would like to initiate.
+    * @returnThe Kernel instance (this) so we can chain calls to put(arr).execute(range).get(arr)
+    * 
+    */
+   public synchronized Kernel execute(int _range, int _passes) {
+      return (execute(Range.create(_range), _passes));
    }
 
    /**
@@ -1480,12 +1639,12 @@ public abstract class Kernel implements Cloneable{
     * @return The Kernel instance (this) so we can chain calls to put(arr).execute(range).get(arr)
     * 
     */
-   public synchronized Kernel execute(Entry _entry, int _globalSize) {
+   public synchronized Kernel execute(Entry _entry, Range _range) {
       if (kernelRunner == null) {
          kernelRunner = new KernelRunner(this);
 
       }
-      return (kernelRunner.execute(_entry, _globalSize, 1));
+      return (kernelRunner.execute(_entry, _range, 1));
    }
 
    /**
@@ -1499,8 +1658,8 @@ public abstract class Kernel implements Cloneable{
     * @return The Kernel instance (this) so we can chain calls to put(arr).execute(range).get(arr)
     * 
     */
-   public synchronized Kernel execute(String _entrypoint, int _globalSize) {
-      return (execute(_entrypoint, _globalSize, 1));
+   public synchronized Kernel execute(String _entrypoint, Range _range) {
+      return (execute(_entrypoint, _range, 1));
 
    }
 
@@ -1515,12 +1674,12 @@ public abstract class Kernel implements Cloneable{
     * @return The Kernel instance (this) so we can chain calls to put(arr).execute(range).get(arr)
     * 
     */
-   public synchronized Kernel execute(String _entrypoint, int _globalSize, int _passes) {
+   public synchronized Kernel execute(String _entrypoint, Range _range, int _passes) {
       if (kernelRunner == null) {
          kernelRunner = new KernelRunner(this);
 
       }
-      return (kernelRunner.execute(_entrypoint, _globalSize, _passes));
+      return (kernelRunner.execute(_entrypoint, _range, _passes));
 
    }
 
@@ -1693,11 +1852,6 @@ public abstract class Kernel implements Cloneable{
       return (false);
    }
 
-   void setLocalSize(int _localSize) {
-      localSize = _localSize;
-
-   }
-
    // the flag useNullForLocalSize is useful for testing that what we compute for localSize is what OpenCL
    // would also compute if we passed in null.  In non-testing mode, we just call execute with the
    // same localSize that we computed in getLocalSizeJNI.  We don't want do publicize these of course.
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/KernelRunner.java b/com.amd.aparapi/src/java/com/amd/aparapi/KernelRunner.java
index 87e7de46..ab2f07f5 100644
--- a/com.amd.aparapi/src/java/com/amd/aparapi/KernelRunner.java
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/KernelRunner.java
@@ -70,6 +70,9 @@ import com.amd.aparapi.Kernel.EXECUTION_MODE;
  *
  */
 class KernelRunner{
+   /**
+    * Be careful changing the name/type of this field as it is referenced from JNI code.
+    */
    public @interface UsedByJNICode {
 
    }
@@ -305,7 +308,7 @@ class KernelRunner{
     * 
     * @author gfrost
     */
-   @UsedByJNICode public static final int ARG_APARAPI_BUF_IS_DIRECT = 1 << 20;
+   // @UsedByJNICode public static final int ARG_APARAPI_BUF_IS_DIRECT = 1 << 20;
 
    /**
     * This 'bit' indicates that a particular <code>KernelArg</code> represents a <code>char</code> type (array or primitive).
@@ -447,7 +450,7 @@ class KernelRunner{
        * 
        * At present only set for AparapiLocalBuffer objs, JNI multiplies this by localSize
        */
-      @Annotations.Unused @UsedByJNICode public int bytesPerLocalSize;
+      //  @Annotations.Unused @UsedByJNICode public int bytesPerLocalWidth;
 
       /**
        * Only set for array objs, not used on JNI
@@ -527,21 +530,25 @@ class KernelRunner{
     * @param maxJTPLocalSize
     * @return
     */
-   @Annotations.DocMe private native static synchronized long initJNI(Kernel _kernel, int _flags, int numProcessors,
-         int maxJTPLocalSize);
+   @Annotations.DocMe private native static synchronized long initJNI(Kernel _kernel, int _flags);
 
    private native long buildProgramJNI(long _jniContextHandle, String _source);
 
    private native int setArgsJNI(long _jniContextHandle, KernelArg[] _args, int argc);
 
-   private native int runKernelJNI(long _jniContextHandle, int _globalSize, int _localSize, boolean _needSync,
-         boolean useNullForLocalSize, int _passes);
+   private native int runKernelJNI(long _jniContextHandle, Range _range, boolean _needSync, int _passes);
 
    private native int disposeJNI(long _jniContextHandle);
 
-   private native int getLocalSizeJNI(long _jniContextHandle, int _globalSize, int localBytesPerLocalId);
+   private native String getExtensionsJNI(long _jniContextHandle);
+
+   private native int getMaxWorkGroupSizeJNI(long _jniContextHandle);
+
+   private native int getMaxWorkItemSizeJNI(long _jniContextHandle, int _index);
+
+   private native int getMaxComputeUnitsJNI(long _jniContextHandle);
 
-   private native String getExtensions(long _jniContextHandle);
+   private native int getMaxWorkItemDimensionsJNI(long _jniContextHandle);
 
    private Set<String> capabilitiesSet;
 
@@ -635,34 +642,6 @@ class KernelRunner{
       return capabilitiesSet.contains(CL_KHR_GL_SHARING);
    }
 
-   private static int numCores = Runtime.getRuntime().availableProcessors();
-
-   private static int maxJTPLocalSize = Config.JTPLocalSizeMultiplier * numCores;
-
-   static int getMaxJTPLocalSize() {
-      return maxJTPLocalSize;
-   }
-
-   /**
-    * We need to match OpenCL's algorithm for localsize.
-    * 
-    * @param _globalSize
-    *          The globalsize requested by the user (via <code>Kernel.execute(globalSize)</code>)
-    * @return The value we use for JTP execution for localSize
-    */
-   private static int getJTPLocalSizeForGlobalSize(int _globalSize) {
-      // iterate down until we find a localSize that divides _globalSize equally
-      for (int localSize = getMaxJTPLocalSize(); localSize > 0; localSize--) {
-         if (_globalSize % localSize == 0) {
-            if (logger.isLoggable(Level.FINE)) {
-               logger.fine("executeJava: picking localSize=" + localSize + " for globalSize=" + _globalSize);
-            }
-            return localSize;
-         }
-      }
-      return 0;
-   }
-
    /**
     * Execute using a Java thread pool. Either because we were explicitly asked to do so, or because we 'fall back' after discovering an OpenCL issue.
     * 
@@ -672,108 +651,272 @@ class KernelRunner{
     *          The # of passes requested by the user (via <code>Kernel.execute(globalSize, passes)</code>). Note this is usually defaulted to 1 via <code>Kernel.execute(globalSize)</code>.
     * @return
     */
-   private long executeJava(final int _globalSize, final int _passes) {
+   private long executeJava(final Range _range, final int _passes) {
       if (logger.isLoggable(Level.FINE)) {
-         logger.fine("executeJava: _globalSize=" + _globalSize);
+         logger.fine("executeJava: range = " + _range);
       }
 
       if (kernel.getExecutionMode().equals(EXECUTION_MODE.SEQ)) {
 
-         kernel.localBarrier = new CyclicBarrier(1);
-
-         kernel.setSizes(_globalSize, 1);
+         /**
+          * SEQ mode is useful for testing trivial logic, but kernels which use SEQ mode cannot be used if the
+          * product of localSize(0..3) is >1.  So we can use multi-dim ranges but only if the local size is 1 in all dimensions. 
+          * 
+          * As a result of this barrier is only ever 1 work item wide and probably should be turned into a no-op. 
+          * 
+          * So we need to check if the range is valid here. If not we have no choice but to punt.
+          */
+         if (_range.getLocalSize(0) * _range.getLocalSize(1) * _range.getLocalSize(2) > 1) {
+            throw new IllegalStateException("Can't run range with group size >1 sequentially. Barriers would deadlock!");
+         }
 
-         kernel.setNumGroups(_globalSize);
-         Kernel worker = (Kernel) kernel.clone();
-         for (int passid = 0; passid < _passes; passid++) {
-            worker.setPassId(passid);
-            for (int id = 0; id < _globalSize; id++) {
-               worker.setGroupId(id);
-               worker.setGlobalId(id);
-               worker.setLocalId(0);
-               worker.run();
+         Kernel kernelClone = (Kernel) kernel.clone();
+         kernelClone.range = _range;
+         kernelClone.groupId[0] = 0;
+         kernelClone.groupId[1] = 0;
+         kernelClone.groupId[2] = 0;
+         kernelClone.localId[0] = 0;
+         kernelClone.localId[1] = 0;
+         kernelClone.localId[2] = 0;
+         kernelClone.localBarrier = new CyclicBarrier(1);
+         for (kernelClone.passId = 0; kernelClone.passId < _passes; kernelClone.passId++) {
+
+            if (_range.getDims() == 1) {
+               for (int id = 0; id < _range.getGlobalSize(0); id++) {
+                  kernelClone.globalId[0] = id;
+                  kernelClone.run();
+               }
+            } else if (_range.getDims() == 2) {
+               for (int x = 0; x < _range.getGlobalSize(0); x++) {
+                  kernelClone.globalId[0] = x;
+                  for (int y = 0; y < _range.getGlobalSize(1); y++) {
+                     kernelClone.globalId[1] = y;
+                     kernelClone.run();
+                  }
+               }
+            } else if (_range.getDims() == 3) {
+               for (int x = 0; x < _range.getGlobalSize(0); x++) {
+                  kernelClone.globalId[0] = x;
+                  for (int y = 0; y < _range.getGlobalSize(1); y++) {
+                     kernelClone.globalId[1] = y;
+                     for (int z = 0; z < _range.getGlobalSize(2); z++) {
+                        kernelClone.globalId[2] = z;
+                        kernelClone.run();
+                     }
+                     kernelClone.run();
+                  }
+               }
             }
          }
+
       } else {
-         // note uses of final so we can use in anonymous inner class
-         final int localSize = getJTPLocalSizeForGlobalSize(_globalSize);
-         // if (localSize == 0) return 0; // should never happen
-         final int numGroups = _globalSize / localSize;
-
-         // compute numThreadSets by multiplying localSize until bigger than numCores
-         final int numThreadSets = localSize >= numCores ? 1 : (numCores + (localSize - 1)) / localSize;
-         final int numThreads = numThreadSets * localSize;
-         // when dividing to get groupsPerThreadSet, round up
-         final int groupsPerThreadSet = (numGroups + (numThreadSets - 1)) / numThreadSets;
-         if (logger.isLoggable(Level.FINE)) {
-            logger.fine("executeJava: localSize=" + localSize + ", numThreads=" + numThreads + ", numThreadSets=" + numThreadSets
-                  + ", numGroups=" + numGroups);
-         }
 
-         Thread[] threads = new Thread[numThreads];
-         // joinBarrier that says all threads are finished
-         final CyclicBarrier joinBarrier = new CyclicBarrier(numThreads + 1);
-
-         // each threadSet shares a CyclicBarrier of size localSize
-         final CyclicBarrier localBarriers[] = new CyclicBarrier[numThreadSets];
-         kernel.setSizes(_globalSize, localSize);
-         kernel.setNumGroups(numGroups);
-         for (int passid = 0; passid < _passes; passid++) {
-            kernel.setPassId(passid);
-            for (int thrSetId = 0; thrSetId < numThreadSets; thrSetId++) {
-               final int startGroupId = thrSetId * groupsPerThreadSet;
-               final int endGroupId = Math.min((thrSetId + 1) * groupsPerThreadSet, numGroups);
-               // System.out.println("thrSetId=" + thrSetId + " running groups from " + startGroupId + " thru " + (endGroupId-1));
-               localBarriers[thrSetId] = new CyclicBarrier(localSize);
-
-               // each threadSet has localSize threads
-               for (int lid = 0; lid < localSize; lid++) { // for each thread in threadSet
-                  final int localId = lid;
-                  final int threadId = thrSetId * localSize + localId;
-                  final Kernel worker = (Kernel) kernel.clone();
-                  worker.setLocalId(localId);
-                  worker.localBarrier = localBarriers[thrSetId]; // barrier that the kernel has access to
-
-                  threads[threadId] = new Thread(new Runnable(){
-                     @Override public void run() {
-                        for (int groupId = startGroupId; groupId < endGroupId; groupId++) {
-                           int globalId = (groupId * localSize) + localId;
-                           worker.setGroupId(groupId);
-                           worker.setGlobalId(globalId);
-                           // System.out.println("running worker with gid=" + globalId + ", lid=" + localId
-                           // + ", groupId=" + groupId + ", threadId=" + threadId);
-                           worker.run();
-                        }
-                        try {
-                           joinBarrier.await();
-                        } catch (InterruptedException e) {
-                           // TODO Auto-generated catch block
-                           e.printStackTrace();
-                        } catch (BrokenBarrierException e) {
-                           // TODO Auto-generated catch block
-                           e.printStackTrace();
-                        }
+         final int threads = _range.getLocalSize(0) * _range.getLocalSize(1) * _range.getLocalSize(2);
+         final int globalGroups = _range.getNumGroups(0) * _range.getNumGroups(1) * _range.getNumGroups(2);
+         final Thread threadArray[] = new Thread[threads];
+         /**
+          * This joinBarrier is the barrier that we provide for the kernel threads to rendezvous with the current dispatch thread.
+          * So this barrier is threadCount+1 wide (the +1 is for the dispatch thread)
+          */
+         final CyclicBarrier joinBarrier = new CyclicBarrier(threads + 1);
+
+         /**
+          * This localBarrier is only ever used by the kernels.  If the kernel does not use the barrier the threads 
+          * can get out of sync, we promised nothing in JTP mode.
+          *
+          * As with OpenCL all threads within a group must wait at the barrier or none.  It is a user error (possible deadlock!)
+          * if the barrier is in a conditional that is only executed by some of the threads within a group.
+          * 
+          * Kernel developer must understand this.
+          * 
+          * This barrier is threadCount wide.  We never hit the barrier from the dispatch thread.
+          */
+         final CyclicBarrier localBarrier = new CyclicBarrier(threads);
+
+         /**
+           * Note that we emulate OpenCL by creating one thread per localId (across the group).
+           * 
+           * So threadCount == range.getLocalSize(0)*range.getLocalSize(1)*range.getLocalSize(2);
+           * 
+           * For a 1D range of 12 groups of 4 we create 4 threads. One per localId(0).
+           * 
+           * We also clone the kernel 4 times. One per thread.
+           * 
+           * We create local barrier which has a width of 4
+           *         
+           *    Thread-0 handles localId(0) (global 0,4,8)
+           *    Thread-1 handles localId(1) (global 1,5,7)
+           *    Thread-2 handles localId(2) (global 2,6,10)
+           *    Thread-3 handles localId(3) (global 3,7,11)
+           *    
+           * This allows all threads to synchronize using the local barrier.
+           * 
+           * Initially the use of local buffers seems broken as the buffers appears to be per Kernel.
+           * Thankfully Kernel.clone() performs a shallow clone of all buffers (local and global)
+           * So each of the cloned kernels actually still reference the same underlying local/global buffers. 
+           * 
+           * If the kernel uses local buffers but does not use barriers then it is possible for different groups
+           * to see mutations from each other (unlike OpenCL), however if the kernel does not us barriers then it 
+           * cannot assume any coherence in OpenCL mode either (the failure mode will be different but still wrong) 
+           * 
+           * So even JTP mode use of local buffers will need to use barriers. Not for the same reason as OpenCL but to keep groups in lockstep.
+           * 
+           **/
+
+         for (int id = 0; id < threads; id++) {
+            final int threadId = id;
+
+            /**
+             *  We clone one kernel for each thread.
+             *  
+             *  They will all share references to the same range, localBarrier and global/local buffers because the clone is shallow.
+             *  We need clones so that each thread can assign 'state' (localId/globalId/groupId) without worrying 
+             *  about other threads.   
+             */
+            final Kernel kernelClone = (Kernel) kernel.clone();
+            kernelClone.range = _range;
+            kernelClone.localBarrier = localBarrier;
+
+            threadArray[threadId] = new Thread(new Runnable(){
+               @Override public void run() {
+                  for (int globalGroupId = 0; globalGroupId < globalGroups; globalGroupId++) {
+
+                     if (_range.getDims() == 1) {
+                        kernelClone.localId[0] = threadId % _range.getLocalSize(0);
+                        kernelClone.globalId[0] = threadId + globalGroupId * threads;
+                        kernelClone.groupId[0] = globalGroupId;
+                     } else if (_range.getDims() == 2) {
+
+                        /**
+                         * Consider a 12x4 grid of 4*2 local groups
+                         * <pre>
+                         *                                             threads = 4*2 = 8
+                         *                                             localWidth=4
+                         *                                             localHeight=2
+                         *                                             globalWidth=12
+                         *                                             globalHeight=4
+                         * 
+                         *    00 01 02 03 | 04 05 06 07 | 08 09 10 11  
+                         *    12 13 14 15 | 16 17 18 19 | 20 21 22 23
+                         *    ------------+-------------+------------
+                         *    24 25 26 27 | 28 29 30 31 | 32 33 34 35
+                         *    36 37 38 39 | 40 41 42 43 | 44 45 46 47  
+                         *    
+                         *    00 01 02 03 | 00 01 02 03 | 00 01 02 03  threadIds : [0..7]*6
+                         *    04 05 06 07 | 04 05 06 07 | 04 05 06 07
+                         *    ------------+-------------+------------
+                         *    00 01 02 03 | 00 01 02 03 | 00 01 02 03
+                         *    04 05 06 07 | 04 05 06 07 | 04 05 06 07  
+                         *    
+                         *    00 00 00 00 | 01 01 01 01 | 02 02 02 02  groupId[0] : 0..6 
+                         *    00 00 00 00 | 01 01 01 01 | 02 02 02 02   
+                         *    ------------+-------------+------------
+                         *    00 00 00 00 | 01 01 01 01 | 02 02 02 02  
+                         *    00 00 00 00 | 01 01 01 01 | 02 02 02 02
+                         *    
+                         *    00 00 00 00 | 00 00 00 00 | 00 00 00 00  groupId[1] : 0..6 
+                         *    00 00 00 00 | 00 00 00 00 | 00 00 00 00   
+                         *    ------------+-------------+------------
+                         *    01 01 01 01 | 01 01 01 01 | 01 01 01 01 
+                         *    01 01 01 01 | 01 01 01 01 | 01 01 01 01
+                         *         
+                         *    00 01 02 03 | 08 09 10 11 | 16 17 18 19  globalThreadIds == threadId + groupId * threads;
+                         *    04 05 06 07 | 12 13 14 15 | 20 21 22 23
+                         *    ------------+-------------+------------
+                         *    24 25 26 27 | 32[33]34 35 | 40 41 42 43
+                         *    28 29 30 31 | 36 37 38 39 | 44 45 46 47   
+                         *          
+                         *    00 01 02 03 | 00 01 02 03 | 00 01 02 03  localX = threadId % localWidth; (for globalThreadId 33 = threadId = 01 : 01%4 =1)
+                         *    00 01 02 03 | 00 01 02 03 | 00 01 02 03   
+                         *    ------------+-------------+------------
+                         *    00 01 02 03 | 00[01]02 03 | 00 01 02 03 
+                         *    00 01 02 03 | 00 01 02 03 | 00 01 02 03
+                         *     
+                         *    00 00 00 00 | 00 00 00 00 | 00 00 00 00  localY = threadId /localWidth  (for globalThreadId 33 = threadId = 01 : 01/4 =0)
+                         *    01 01 01 01 | 01 01 01 01 | 01 01 01 01   
+                         *    ------------+-------------+------------
+                         *    00 00 00 00 | 00[00]00 00 | 00 00 00 00 
+                         *    01 01 01 01 | 01 01 01 01 | 01 01 01 01
+                         *     
+                         *    00 01 02 03 | 04 05 06 07 | 08 09 10 11  globalX=
+                         *    00 01 02 03 | 04 05 06 07 | 08 09 10 11     groupsPerLineWidth=globalWidth/localWidth (=12/4 =3)
+                         *    ------------+-------------+------------     groupInset =groupId%groupsPerLineWidth (=4%3 = 1)
+                         *    00 01 02 03 | 04[05]06 07 | 08 09 10 11 
+                         *    00 01 02 03 | 04 05 06 07 | 08 09 10 11     globalX = groupInset*localWidth+localX (= 1*4+1 = 5)
+                         *     
+                         *    00 00 00 00 | 00 00 00 00 | 00 00 00 00  globalY
+                         *    01 01 01 01 | 01 01 01 01 | 01 01 01 01      
+                         *    ------------+-------------+------------
+                         *    02 02 02 02 | 02[02]02 02 | 02 02 02 02 
+                         *    03 03 03 03 | 03 03 03 03 | 03 03 03 03
+                         *    
+                         * </pre>
+                         * Assume we are trying to locate the id's for #33 
+                         *
+                         */
+
+                        kernelClone.localId[0] = threadId % _range.getLocalSize(0); // threadId % localWidth =  (for 33 = 1 % 4 = 1)
+                        kernelClone.localId[1] = threadId / _range.getLocalSize(0); // threadId / localWidth = (for 33 = 1 / 4 == 0)
+
+                        int groupInset = globalGroupId % _range.getNumGroups(0); // 4%3 = 1
+                        kernelClone.globalId[0] = groupInset * _range.getLocalSize(0) + kernelClone.localId[0]; // 1*4+1=5
+
+                        int completeLines = (globalGroupId / _range.getNumGroups(0)) * _range.getLocalSize(1);// (4/3) * 2
+                        kernelClone.globalId[1] = completeLines + kernelClone.localId[1]; // 2+0 = 2
+                        kernelClone.groupId[0] = globalGroupId % _range.getNumGroups(0);
+                        kernelClone.groupId[1] = globalGroupId / _range.getNumGroups(0);
+                     } else if (_range.getDims() == 3) {
+
+                        //Same as 2D actually turns out that localId[0] is identical for all three dims so could be hoisted out of conditional code
+
+                        kernelClone.localId[0] = threadId % _range.getLocalSize(0);
+
+                        kernelClone.localId[1] = (threadId / _range.getLocalSize(0)) % _range.getLocalSize(1);
+
+                        // the thread id's span WxHxD so threadId/(WxH) should yield the local depth  
+                        kernelClone.localId[2] = threadId / (_range.getLocalSize(0) * _range.getLocalSize(1));
+
+                        kernelClone.globalId[0] = (globalGroupId % _range.getNumGroups(0)) * _range.getLocalSize(0)
+                              + kernelClone.localId[0];
+
+                        kernelClone.globalId[1] = ((globalGroupId / _range.getNumGroups(0)) * _range.getLocalSize(1))
+                              % _range.getGlobalSize(1) + kernelClone.localId[1];
+
+                        kernelClone.globalId[2] = (globalGroupId / (_range.getNumGroups(0) * _range.getNumGroups(1)))
+                              * _range.getLocalSize(2) + kernelClone.localId[2];
+
+                        kernelClone.groupId[0] = globalGroupId % _range.getNumGroups(0);
+                        kernelClone.groupId[1] = (globalGroupId / _range.getNumGroups(0)) % _range.getNumGroups(1);
+                        kernelClone.groupId[2] = globalGroupId / (_range.getNumGroups(0) * _range.getNumGroups(1));
                      }
-                  });
-                  threads[threadId].start();
-               }
+                     kernelClone.run();
 
-               // this is where the main thread waits on the join barrier
-               try {
-                  joinBarrier.await();
-               } catch (InterruptedException e) {
-                  // TODO Auto-generated catch block
-                  e.printStackTrace();
-               } catch (BrokenBarrierException e) {
-                  // TODO Auto-generated catch block
-                  e.printStackTrace();
+                  }
+                  await(joinBarrier); // This thread will rendezvous with dispatch thread here. This is effectively a join.                  
                }
-            }
+            });
+            threadArray[threadId].setName("aparapi-" + threadId + "/" + threads);
+            threadArray[threadId].start();
+
          }
+         await(joinBarrier); // This dispatch thread waits for all worker threads here. 
+
       } // execution mode == JTP
       return 0;
    }
 
+   private static void await(CyclicBarrier _barrier) {
+      try {
+         _barrier.await();
+      } catch (InterruptedException e) {
+         // TODO Auto-generated catch block
+         e.printStackTrace();
+      } catch (BrokenBarrierException e) {
+         // TODO Auto-generated catch block
+         e.printStackTrace();
+      }
+   }
+
    private KernelArg[] args = null;
 
    private boolean usesOopConversion = false;
@@ -1036,12 +1179,8 @@ class KernelRunner{
       }
    }
 
-   // this routine now also finds out how many perLocalItem bytes are specified for this kernel
-   private int localBytesPerLocalId = 0;
-
    private boolean updateKernelArrayRefs() throws AparapiException {
       boolean needsSync = false;
-      localBytesPerLocalId = 0;
 
       for (int i = 0; i < argc; i++) {
          KernelArg arg = args[i];
@@ -1089,37 +1228,59 @@ class KernelRunner{
 
    // private int numAvailableProcessors = Runtime.getRuntime().availableProcessors();
 
-   private Kernel executeOpenCL(final String _entrypointName, final int _globalSize, final int _passes) throws AparapiException {
+   private Kernel executeOpenCL(final String _entrypointName, final Range _range, final int _passes) throws AparapiException {
+      if (_range.getDims() > getMaxWorkItemDimensionsJNI(jniContextHandle)) {
+         throw new RangeException("Range dim size " + _range.getDims() + " > device "
+               + getMaxWorkItemDimensionsJNI(jniContextHandle));
+      }
+      if (_range.getWorkGroupSize() > getMaxWorkGroupSizeJNI(jniContextHandle)) {
+         throw new RangeException("Range workgroup size " + _range.getWorkGroupSize() + " > device "
+               + getMaxWorkGroupSizeJNI(jniContextHandle));
+      }
+      /*
+            if (_range.getGlobalSize(0) > getMaxWorkItemSizeJNI(jniContextHandle, 0)) {
+               throw new RangeException("Range globalsize 0 " + _range.getGlobalSize(0) + " > device "
+                     + getMaxWorkItemSizeJNI(jniContextHandle, 0));
+            }
+            if (_range.getDims() > 1) {
+               if (_range.getGlobalSize(1) > getMaxWorkItemSizeJNI(jniContextHandle, 1)) {
+                  throw new RangeException("Range globalsize 1 " + _range.getGlobalSize(1) + " > device "
+                        + getMaxWorkItemSizeJNI(jniContextHandle, 1));
+               }
+               if (_range.getDims() > 2) {
+                  if (_range.getGlobalSize(2) > getMaxWorkItemSizeJNI(jniContextHandle, 2)) {
+                     throw new RangeException("Range globalsize 2 " + _range.getGlobalSize(2) + " > device "
+                           + getMaxWorkItemSizeJNI(jniContextHandle, 2));
+                  }
+               }
+            }
+      */
+
+      if (logger.isLoggable(Level.FINE)) {
+         logger.fine("maxComputeUnits=" + this.getMaxComputeUnitsJNI(jniContextHandle));
+         logger.fine("maxWorkGroupSize=" + this.getMaxWorkGroupSizeJNI(jniContextHandle));
+         logger.fine("maxWorkItemDimensions=" + this.getMaxWorkItemDimensionsJNI(jniContextHandle));
+         logger.fine("maxWorkItemSize(0)=" + getMaxWorkItemSizeJNI(jniContextHandle, 0));
+         if (_range.getDims() > 1) {
+            logger.fine("maxWorkItemSize(1)=" + getMaxWorkItemSizeJNI(jniContextHandle, 1));
+            if (_range.getDims() > 2) {
+               logger.fine("maxWorkItemSize(2)=" + getMaxWorkItemSizeJNI(jniContextHandle, 2));
+            }
+         }
+      }
 
       // Read the array refs after kernel may have changed them
       // We need to do this as input to computing the localSize
       assert args != null : "args should not be null";
       boolean needSync = updateKernelArrayRefs();
-
-      // note: the above will also recompute the value localBytesPerLocalId
-
-      int localSize = getLocalSizeJNI(jniContextHandle, _globalSize, localBytesPerLocalId);
-      if (localSize == 0) {
-         // should fall back to java?
-         logger.warning("getLocalSizeJNI failed, reverting java");
-         kernel.setFallbackExecutionMode();
-         return execute(_entrypointName, _globalSize, _passes);
-      }
-      assert localSize <= _globalSize : "localSize = " + localSize;
-
-      // Call back to kernel for last minute changes
-      kernel.setSizes(_globalSize, localSize);
-
       if (needSync && logger.isLoggable(Level.FINE)) {
          logger.fine("Need to resync arrays on " + kernel.getClass().getName());
       }
-
       // native side will reallocate array buffers if necessary
-      if (runKernelJNI(jniContextHandle, _globalSize, localSize, needSync, kernel.useNullForLocalSize, _passes) != 0) {
-         //System.out.println("CL exec seems to have failed");
+      if (runKernelJNI(jniContextHandle, _range, needSync, _passes) != 0) {
          logger.warning("### CL exec seems to have failed. Trying to revert to Java ###");
          kernel.setFallbackExecutionMode();
-         return execute(_entrypointName, _globalSize, _passes);
+         return execute(_entrypointName, _range, _passes);
       }
 
       if (usesOopConversion == true) {
@@ -1127,42 +1288,41 @@ class KernelRunner{
       }
 
       if (logger.isLoggable(Level.FINE)) {
-         logger.fine("executeOpenCL completed. _globalSize=" + _globalSize);
+         logger.fine("executeOpenCL completed. " + _range);
       }
       return kernel;
    }
 
-   synchronized Kernel execute(Kernel.Entry entry, final int _globalSize, final int _passes) {
+   synchronized Kernel execute(Kernel.Entry entry, final Range _range, final int _passes) {
       System.out.println("execute(Kernel.Entry, size) not implemented");
       return (kernel);
    }
 
-   synchronized private Kernel fallBackAndExecute(String _entrypointName, final int _globalSize, final int _passes) {
+   synchronized private Kernel fallBackAndExecute(String _entrypointName, final Range _range, final int _passes) {
 
       kernel.setFallbackExecutionMode();
-      return execute(_entrypointName, _globalSize, _passes);
+      return execute(_entrypointName, _range, _passes);
    }
 
-   synchronized private Kernel warnFallBackAndExecute(String _entrypointName, final int _globalSize, final int _passes,
+   synchronized private Kernel warnFallBackAndExecute(String _entrypointName, final Range _range, final int _passes,
          Exception _exception) {
       if (logger.isLoggable(Level.WARNING)) {
          logger.warning("Reverting to Java Thread Pool (JTP) for " + kernel.getClass() + ": " + _exception.getMessage());
          _exception.printStackTrace();
       }
-      return fallBackAndExecute(_entrypointName, _globalSize, _passes);
+      return fallBackAndExecute(_entrypointName, _range, _passes);
    }
 
-   synchronized private Kernel warnFallBackAndExecute(String _entrypointName, final int _globalSize, final int _passes,
-         String _excuse) {
+   synchronized private Kernel warnFallBackAndExecute(String _entrypointName, final Range _range, final int _passes, String _excuse) {
       logger.warning("Reverting to Java Thread Pool (JTP) for " + kernel.getClass() + ": " + _excuse);
-      return fallBackAndExecute(_entrypointName, _globalSize, _passes);
+      return fallBackAndExecute(_entrypointName, _range, _passes);
    }
 
-   synchronized Kernel execute(String _entrypointName, final int _globalSize, final int _passes) {
+   synchronized Kernel execute(String _entrypointName, final Range _range, final int _passes) {
 
       long executeStartTime = System.currentTimeMillis();
-      if (_globalSize == 0) {
-         throw new IllegalStateException("global size can't be 0");
+      if (_range == null) {
+         throw new IllegalStateException("range can't be null");
       }
 
       if (kernel.getExecutionMode().isOpenCL()) {
@@ -1173,7 +1333,7 @@ class KernelRunner{
                entryPoint = classModel.getEntrypoint(_entrypointName, kernel);
             } catch (Exception exception) {
 
-               return warnFallBackAndExecute(_entrypointName, _globalSize, _passes, exception);
+               return warnFallBackAndExecute(_entrypointName, _range, _passes, exception);
             }
             if ((entryPoint != null) && !entryPoint.shouldFallback()) {
 
@@ -1184,12 +1344,12 @@ class KernelRunner{
                jniFlags |= (kernel.getExecutionMode().equals(EXECUTION_MODE.GPU) ? JNI_FLAG_USE_GPU : 0);
                // Init the device to check capabilities before emitting the
                // code that requires the capabilities.
-               jniContextHandle = initJNI(kernel, jniFlags, Runtime.getRuntime().availableProcessors(), getMaxJTPLocalSize());
+               jniContextHandle = initJNI(kernel, jniFlags);
                if (jniContextHandle == 0) {
-                  return warnFallBackAndExecute(_entrypointName, _globalSize, _passes, "initJNI failed to return a valid handle");
+                  return warnFallBackAndExecute(_entrypointName, _range, _passes, "initJNI failed to return a valid handle");
                }
 
-               String extensions = getExtensions(jniContextHandle);
+               String extensions = getExtensionsJNI(jniContextHandle);
                capabilitiesSet = new HashSet<String>();
                StringTokenizer strTok = new StringTokenizer(extensions);
                while (strTok.hasMoreTokens()) {
@@ -1201,12 +1361,12 @@ class KernelRunner{
 
                if (entryPoint.requiresDoublePragma() && !hasFP64Support()) {
 
-                  return warnFallBackAndExecute(_entrypointName, _globalSize, _passes, "FP64 required but not supported");
+                  return warnFallBackAndExecute(_entrypointName, _range, _passes, "FP64 required but not supported");
                }
 
                if (entryPoint.requiresByteAddressableStorePragma() && !hasByteAddressableStoreSupport()) {
 
-                  return warnFallBackAndExecute(_entrypointName, _globalSize, _passes,
+                  return warnFallBackAndExecute(_entrypointName, _range, _passes,
                         "Byte addressable stores required but not supported");
                }
 
@@ -1215,24 +1375,16 @@ class KernelRunner{
 
                if (entryPoint.requiresAtomic32Pragma() && !all32AtomicsAvailable) {
 
-                  return warnFallBackAndExecute(_entrypointName, _globalSize, _passes, "32 bit Atomics required but not supported");
+                  return warnFallBackAndExecute(_entrypointName, _range, _passes, "32 bit Atomics required but not supported");
                }
 
-               final StringBuilder openCLStringBuilder = new StringBuilder();
-               KernelWriter openCLWriter = new KernelWriter(){
-                  @Override public void write(String _string) {
-                     openCLStringBuilder.append(_string);
-                  }
-               };
-
-               // Emit the OpenCL source into a string
+               String openCL = null;
                try {
-                  openCLWriter.write(entryPoint);
-
+                  openCL = KernelWriter.writeToString(entryPoint);
                } catch (CodeGenException codeGenException) {
-                  return warnFallBackAndExecute(_entrypointName, _globalSize, _passes, codeGenException);
+                  return warnFallBackAndExecute(_entrypointName, _range, _passes, codeGenException);
                }
-               String openCL = openCLStringBuilder.toString();
+
                if (Config.enableShowGeneratedOpenCL) {
                   System.out.println(openCL);
                }
@@ -1241,9 +1393,8 @@ class KernelRunner{
                }
 
                // Send the string to OpenCL to compile it
-               if (buildProgramJNI(jniContextHandle, openCLStringBuilder.toString()) == 0) {
-
-                  return warnFallBackAndExecute(_entrypointName, _globalSize, _passes, "OpenCL compile failed");
+               if (buildProgramJNI(jniContextHandle, openCL) == 0) {
+                  return warnFallBackAndExecute(_entrypointName, _range, _passes, "OpenCL compile failed");
                }
 
                args = new KernelArg[entryPoint.getReferencedFields().size()];
@@ -1256,9 +1407,15 @@ class KernelRunner{
                      args[i].name = field.getName();
                      args[i].field = field;
                      args[i].isStatic = (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC;
-
                      Class<?> type = field.getType();
                      if (type.isArray()) {
+
+                        if (field.getAnnotation(com.amd.aparapi.Kernel.Local.class) != null
+                              || args[i].name.endsWith(Kernel.LOCAL_SUFFIX)) {
+                           args[i].type |= ARG_LOCAL;
+                        } else {
+                           args[i].type |= ARG_GLOBAL;
+                        }
                         args[i].array = null; // will get updated in updateKernelArrayRefs
                         args[i].type |= ARG_ARRAY;
                         if (isExplicit()) {
@@ -1269,7 +1426,7 @@ class KernelRunner{
                         args[i].type |= entryPoint.getArrayFieldAssignments().contains(field.getName()) ? (ARG_WRITE | ARG_READ)
                               : 0;
                         args[i].type |= entryPoint.getArrayFieldAccesses().contains(field.getName()) ? ARG_READ : 0;
-                        args[i].type |= ARG_GLOBAL;
+                        // args[i].type |= ARG_GLOBAL;
                         args[i].type |= type.isAssignableFrom(float[].class) ? ARG_FLOAT : 0;
 
                         args[i].type |= type.isAssignableFrom(int[].class) ? ARG_INT : 0;
@@ -1351,26 +1508,26 @@ class KernelRunner{
                conversionTime = System.currentTimeMillis() - executeStartTime;
 
                try {
-                  executeOpenCL(_entrypointName, _globalSize, _passes);
+                  executeOpenCL(_entrypointName, _range, _passes);
                } catch (AparapiException e) {
-                  warnFallBackAndExecute(_entrypointName, _globalSize, _passes, e);
+                  warnFallBackAndExecute(_entrypointName, _range, _passes, e);
                }
             } else {
-               warnFallBackAndExecute(_entrypointName, _globalSize, _passes, "failed to locate entrypoint");
+               warnFallBackAndExecute(_entrypointName, _range, _passes, "failed to locate entrypoint");
             }
 
          } else {
 
             try {
-               executeOpenCL(_entrypointName, _globalSize, _passes);
+               executeOpenCL(_entrypointName, _range, _passes);
             } catch (AparapiException e) {
 
-               warnFallBackAndExecute(_entrypointName, _globalSize, _passes, e);
+               warnFallBackAndExecute(_entrypointName, _range, _passes, e);
             }
          }
 
       } else {
-         executeJava(_globalSize, _passes);
+         executeJava(_range, _passes);
       }
       if (Config.enableExecutionModeReporting) {
          System.out.println(kernel.getClass().getCanonicalName() + ":" + kernel.getExecutionMode());
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/KernelWriter.java b/com.amd.aparapi/src/java/com/amd/aparapi/KernelWriter.java
index 7a1886bb..a9eb0f28 100644
--- a/com.amd.aparapi/src/java/com/amd/aparapi/KernelWriter.java
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/KernelWriter.java
@@ -43,10 +43,11 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import com.amd.aparapi.ClassModel.ClassModelField;
 import com.amd.aparapi.ClassModel.AttributePool.LocalVariableTableEntry;
-import com.amd.aparapi.ClassModel.AttributePool.LocalVariableTableEntry.LocalVariableInfo;
 import com.amd.aparapi.ClassModel.AttributePool.RuntimeAnnotationsEntry;
-import com.amd.aparapi.ClassModel.ClassModelField;
+import com.amd.aparapi.ClassModel.AttributePool.LocalVariableTableEntry.LocalVariableInfo;
+import com.amd.aparapi.ClassModel.AttributePool.RuntimeAnnotationsEntry.AnnotationInfo;
 import com.amd.aparapi.ClassModel.ConstantPool.FieldEntry;
 import com.amd.aparapi.ClassModel.ConstantPool.MethodEntry;
 import com.amd.aparapi.InstructionSet.AccessArrayElement;
@@ -92,14 +93,47 @@ abstract class KernelWriter extends BlockWriter{
 
    final static Map<String, String> javaToCLIdentifierMap = new HashMap<String, String>();
    {
+
       javaToCLIdentifierMap.put("getGlobalId()I", "get_global_id(0)");
+      javaToCLIdentifierMap.put("getGlobalId(I)I", "get_global_id"); // no parenthesis if we are conveying args
+      javaToCLIdentifierMap.put("getGlobalX()I", "get_global_id(0)");
+      javaToCLIdentifierMap.put("getGlobalY()I", "get_global_id(1)");
+      javaToCLIdentifierMap.put("getGlobalZ()I", "get_global_id(2)");
+
       javaToCLIdentifierMap.put("getGlobalSize()I", "get_global_size(0)");
+      javaToCLIdentifierMap.put("getGlobalSize(I)I", "get_global_size"); // no parenthesis if we are conveying args
+      javaToCLIdentifierMap.put("getGlobalWidth()I", "get_global_size(0)");
+      javaToCLIdentifierMap.put("getGlobalHeight()I", "get_global_size(1)");
+      javaToCLIdentifierMap.put("getGlobalDepth()I", "get_global_size(2)");
+
       javaToCLIdentifierMap.put("getLocalId()I", "get_local_id(0)");
+      javaToCLIdentifierMap.put("getLocalId(I)I", "get_local_id"); // no parenthesis if we are conveying args
+      javaToCLIdentifierMap.put("getLocalX()I", "get_local_id(0)");
+      javaToCLIdentifierMap.put("getLocalY()I", "get_local_id(1)");
+      javaToCLIdentifierMap.put("getLocalZ()I", "get_local_id(2)");
+
       javaToCLIdentifierMap.put("getLocalSize()I", "get_local_size(0)");
+      javaToCLIdentifierMap.put("getLocalSize(I)I", "get_local_size"); // no parenthesis if we are conveying args
+      javaToCLIdentifierMap.put("getLocalWidth()I", "get_local_size(0)");
+      javaToCLIdentifierMap.put("getLocalHeight()I", "get_local_size(1)");
+      javaToCLIdentifierMap.put("getLocalDepth()I", "get_local_size(2)");
+
       javaToCLIdentifierMap.put("getNumGroups()I", "get_num_groups(0)");
+      javaToCLIdentifierMap.put("getNumGroups(I)I", "get_num_groups"); // no parenthesis if we are conveying args
+      javaToCLIdentifierMap.put("getNumGroupsX()I", "get_num_groups(0)");
+      javaToCLIdentifierMap.put("getNumGroupsY()I", "get_num_groups(1)");
+      javaToCLIdentifierMap.put("getNumGroupsZ()I", "get_num_groups(2)");
+
       javaToCLIdentifierMap.put("getGroupId()I", "get_group_id(0)");
+      javaToCLIdentifierMap.put("getGroupId(I)I", "get_group_id"); // no parenthesis if we are conveying args
+      javaToCLIdentifierMap.put("getGroupX()I", "get_group_id(0)");
+      javaToCLIdentifierMap.put("getGroupY()I", "get_group_id(1)");
+      javaToCLIdentifierMap.put("getGroupZ()I", "get_group_id(2)");
+
       javaToCLIdentifierMap.put("getPassId()I", "get_pass_id(this)");
+
       javaToCLIdentifierMap.put("localBarrier()V", "barrier(CLK_LOCAL_MEM_FENCE)");
+
       javaToCLIdentifierMap.put("globalBarrier()V", "barrier(CLK_GLOBAL_MEM_FENCE)");
 
    }
@@ -160,7 +194,19 @@ abstract class KernelWriter extends BlockWriter{
       if (barrierAndGetterMappings != null) {
          // this is one of the OpenCL barrier or size getter methods
          // write the mapping and exit
-         write(barrierAndGetterMappings);
+         if (argc > 0) {
+            write(barrierAndGetterMappings);
+            write("(");
+            for (int arg = 0; arg < argc; arg++) {
+               if ((arg != 0)) {
+                  write(", ");
+               }
+               writeInstruction(_methodCall.getArg(arg));
+            }
+            write(")");
+         } else {
+            write(barrierAndGetterMappings);
+         }
       } else {
 
          String intrinsicMapping = Kernel.getMappedMethodName(_methodEntry);
@@ -220,14 +266,17 @@ abstract class KernelWriter extends BlockWriter{
       newLine();
    }
 
+   public final static String __local = "__local";
+
+   public final static String __global = "__global";
+
+   public final static String LOCAL_ANNOTATION_NAME = "L" + Kernel.Local.class.getName().replace(".", "/") + ";";
+
    @Override void write(Entrypoint _entryPoint) throws CodeGenException {
       List<String> thisStruct = new ArrayList<String>();
       List<String> argLines = new ArrayList<String>();
       List<String> assigns = new ArrayList<String>();
 
-      // hack
-      // for (java.lang.reflect.Field f:_entryPoint.getTheClass().getDeclaredFields()){
-
       entryPoint = _entryPoint;
 
       for (ClassModelField field : _entryPoint.getReferencedClassModelFields()) {
@@ -239,13 +288,18 @@ abstract class KernelWriter extends BlockWriter{
          String signature = field.getDescriptor();
 
          boolean isPointer = false;
+
+         // check the suffix 
+         String type = field.getName().endsWith(Kernel.LOCAL_SUFFIX) ? __local : __global;
          RuntimeAnnotationsEntry visibleAnnotations = field.fieldAttributePool.getRuntimeVisibleAnnotationsEntry();
 
-         String type = "__global";
          if (visibleAnnotations != null) {
-            // for (AnnotationInfo ai : visibleAnnotations) {
-            // String typeDescriptor = ai.getTypeDescriptor();
-            // }
+            for (AnnotationInfo ai : visibleAnnotations) {
+               String typeDescriptor = ai.getTypeDescriptor();
+               if (typeDescriptor.equals(LOCAL_ANNOTATION_NAME)) {
+                  type = __local;
+               }
+            }
          }
 
          if (signature.startsWith("[")) {
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/MethodModel.java b/com.amd.aparapi/src/java/com/amd/aparapi/MethodModel.java
index 4d590037..96106eea 100644
--- a/com.amd.aparapi/src/java/com/amd/aparapi/MethodModel.java
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/MethodModel.java
@@ -117,8 +117,6 @@ class MethodModel{
    private boolean methodIsGetter;
 
    private boolean methodIsSetter;
-   
-   
 
    // Only setters can use putfield
    private boolean usesPutfield;
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/Range.java b/com.amd.aparapi/src/java/com/amd/aparapi/Range.java
new file mode 100644
index 00000000..d0b59cad
--- /dev/null
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/Range.java
@@ -0,0 +1,392 @@
+package com.amd.aparapi;
+
+import java.util.Arrays;
+
+/**
+ * 
+ * A representation of 1, 2 or 3 dimensional range of execution. 
+ * 
+ * This class uses factory methods to allow one, two or three dimensional ranges to be created. 
+ * <br/>
+ * For a Kernel operating over the linear range 0..1024 without a specified groups size we would create a one dimensional <code>Range</code> using 
+ * <blockquote><pre>Range.create(1024);</pre></blockquote>
+ * To request the same linear range but with a groupSize of 64 (range must be a multiple of group size!) we would use
+ * <blockquote><pre>Range.create(1024,64);</pre></blockquote>
+ * To request a two dimensional range over a grid (0..width)x(0..height) where width==512 and height=256 we would use
+ * <blockquote><pre>
+ * int width=512;
+ * int height=256;
+ * Range.create2D(width,height)
+ * </pre></blockquote>
+ * Again the above does not specify the group size.  One will be chosen for you. If you want to specify the groupSize (say 16x8; 16 wide by 8 high) use
+ * <blockquote><pre>
+ * int width=512;
+ * int height=256;
+ * int groupWidth=16;
+ * int groupHeight=8;
+ * Range.create2D(width, height, groupWidth, groupHeight);
+ * </pre></blockquote>
+ * Finally we can request a three dimensional range using 
+ * <blockquote><pre>
+ * int width=512;
+ * int height=256;
+ * int depth=8;
+ * Range.create3D(width, height, depth);
+ * </pre></blockquote>
+ * And can specify a group size using 
+ * <blockquote><pre>
+ *  int width=512;
+ *  int height=256;
+ *  int depth=8;
+ *  int groupWidth=8;
+ *  int groupHeight=4;
+ *  int groupDepth=2
+ *  Range.create3D(width, height, depth, groupWidth, groupHeight, groupDepth);
+ * </pre></blockquote>
+ */
+public class Range{
+   @KernelRunner.UsedByJNICode private int globalSize_0 = 1;
+
+   @KernelRunner.UsedByJNICode private int localSize_0 = 1;
+
+   @KernelRunner.UsedByJNICode private int globalSize_1 = 1;
+
+   @KernelRunner.UsedByJNICode private int localSize_1 = 1;
+
+   @KernelRunner.UsedByJNICode private int globalSize_2 = 1;
+
+   @KernelRunner.UsedByJNICode private int localSize_2 = 1;
+
+   @KernelRunner.UsedByJNICode private int dims;
+
+   @KernelRunner.UsedByJNICode private boolean valid;
+
+   @KernelRunner.UsedByJNICode private boolean localIsDerived = false;
+
+   /**
+    * Get the localSize (of the group) given the requested dimension
+    * 
+    * @param _dim 0=width, 1=height, 2=depth
+    * @return The size of the group give the requested dimension
+    */
+   public int getLocalSize(int _dim) {
+      return (_dim == 0 ? localSize_0 : (_dim == 1 ? localSize_1 : localSize_2));
+   }
+
+   /**
+    * Get the globalSize (of the range) given the requested dimension
+    * 
+    * @param _dim 0=width, 1=height, 2=depth
+    * @return The size of the group give the requested dimension
+    */
+   public int getGlobalSize(int _dim) {
+      return (_dim == 0 ? globalSize_0 : (_dim == 1 ? globalSize_1 : globalSize_2));
+   }
+
+   private static final int THREADS_PER_CORE = 16;
+
+   private static final int MAX_OPENCL_GROUP_SIZE = 256;
+
+   private static final int MAX_GROUP_SIZE = Math.max(Runtime.getRuntime().availableProcessors() * THREADS_PER_CORE,
+         MAX_OPENCL_GROUP_SIZE);
+
+   /** 
+    * Create a one dimensional range <code>0.._globalWidth</code> which is processed in groups of size _localWidth.
+    * <br/>
+    * Note that for this range to be valid : </br> <strong><code>_globalWidth > 0 && _localWidth > 0 && _localWidth < MAX_GROUP_SIZE && _globalWidth % _localWidth==0</code></strong>
+    * 
+    * @param _globalWidth the overall range we wish to process
+    * @param _localWidth the size of the group we wish to process.
+    * @return A new Range with the requested dimensions
+    */
+   public static Range create(int _globalWidth, int _localWidth) {
+      Range range = new Range();
+      range.dims = 1;
+      range.globalSize_0 = _globalWidth;
+      range.localSize_0 = _localWidth;
+      range.valid = range.localSize_0 > 0 && range.localSize_0 < MAX_GROUP_SIZE && range.globalSize_0 % range.localSize_0 == 0;
+      return (range);
+   }
+
+   /**
+    * Determine the set of factors for a given value.
+    * @param _value The value we wish to factorize. 
+    * @return and array of factors of _value
+    */
+
+   private static int[] getFactors(int _value) {
+      int factors[] = new int[MAX_GROUP_SIZE];
+      int factorIdx = 0;
+      for (int possibleFactor = 1; possibleFactor <= MAX_GROUP_SIZE; possibleFactor++) {
+         if (_value % possibleFactor == 0) {
+            factors[factorIdx++] = possibleFactor;
+         }
+      }
+      return (Arrays.copyOf(factors, factorIdx));
+   }
+
+   /** 
+    * Create a one dimensional range <code>0.._globalWidth</code> with an undefined group size.
+    * <br/>
+    * Note that for this range to be valid :- </br> <strong><code>_globalWidth > 0 </code></strong>
+    * <br/>
+    * The groupsize will be chosen such that _localWidth > 0 && _localWidth < MAX_GROUP_SIZE && _globalWidth % _localWidth==0 is true
+    * 
+    * We extract the factors of _globalWidth and choose the highest value.
+    * 
+    * @param _globalWidth the overall range we wish to process
+    * @return A new Range with the requested dimensions
+    */
+   public static Range create(int _globalWidth) {
+      Range withoutLocal = create(_globalWidth, 1);
+      withoutLocal.localIsDerived = true;
+      int[] factors = getFactors(withoutLocal.globalSize_0);
+
+      withoutLocal.localSize_0 = factors[factors.length - 1];
+
+      withoutLocal.valid = withoutLocal.localSize_0 > 0 && withoutLocal.localSize_0 < MAX_GROUP_SIZE
+            && withoutLocal.globalSize_0 % withoutLocal.localSize_0 == 0;
+      return (withoutLocal);
+   }
+
+   /** 
+    * Create a two dimensional range 0.._globalWidth x 0.._globalHeight using a group which is _localWidth x _localHeight in size.
+    * <br/>
+    * Note that for this range to be valid  _globalWidth > 0 &&  _globalHeight >0 && _localWidth>0 && _localHeight>0 && _localWidth*_localHeight < MAX_GROUP_SIZE && _globalWidth%_localWidth==0 && _globalHeight%_localHeight==0.
+    * 
+    *  @param _globalWidth the overall range we wish to process
+    * @return
+    */
+   public static Range create2D(int _globalWidth, int _globalHeight, int _localWidth, int _localHeight) {
+      Range range = new Range();
+      range.dims = 2;
+      range.globalSize_0 = _globalWidth;
+      range.localSize_0 = _localWidth;
+      range.globalSize_1 = _globalHeight;
+      range.localSize_1 = _localHeight;
+      range.valid = range.localSize_0 > 0 && range.localSize_1 > 0 && range.localSize_0 * range.localSize_1 < MAX_GROUP_SIZE
+            && range.globalSize_0 % range.localSize_0 == 0 && range.globalSize_1 % range.localSize_1 == 0;
+
+      return (range);
+   }
+
+   /** 
+    * Create a two dimensional range <code>0.._globalWidth * 0.._globalHeight</code> choosing suitable values for <code>localWidth</code> and <code>localHeight</code>.
+    * <p>
+    * Note that for this range to be valid  <code>_globalWidth > 0 &&  _globalHeight >0 && _localWidth>0 && _localHeight>0 && _localWidth*_localHeight < MAX_GROUP_SIZE && _globalWidth%_localWidth==0 && _globalHeight%_localHeight==0</code>.
+    * 
+    * <p>
+    * To determine suitable values for <code>_localWidth</code> and <code>_localHeight</code> we extract the factors for <code>_globalWidth</code> and <code>_globalHeight</code> and then 
+    * find the largest product ( <code><= MAX_GROUP_SIZE</code>) with the lowest perimeter.
+    * 
+    * <p>
+    * For example for <code>MAX_GROUP_SIZE</code> of 16 we favor 4x4 over 1x16.
+    * 
+    * @param _globalWidth the overall range we wish to process
+    * @return
+    */
+   public static Range create2D(int _globalWidth, int _globalHeight) {
+      Range withoutLocal = create2D(_globalWidth, _globalHeight, 1, 1);
+      withoutLocal.localIsDerived = true;
+      int[] widthFactors = getFactors(_globalWidth);
+      int[] heightFactors = getFactors(_globalHeight);
+
+      withoutLocal.localSize_0 = 1;
+      withoutLocal.localSize_1 = 1;
+      int max = 1;
+      int perimeter = 0;
+      for (int w : widthFactors) {
+         for (int h : heightFactors) {
+            int size = w * h;
+            if (size > MAX_GROUP_SIZE) {
+               break;
+            }
+
+            if (size > max) {
+               max = size;
+               perimeter = w + h;
+               withoutLocal.localSize_0 = w;
+               withoutLocal.localSize_1 = h;
+            } else if (size == max) {
+               int localPerimeter = w + h;
+               if (localPerimeter < perimeter) {// is this the shortest perimeter so far
+                  perimeter = localPerimeter;
+                  withoutLocal.localSize_0 = w;
+                  withoutLocal.localSize_1 = h;
+               }
+            }
+         }
+      }
+
+      withoutLocal.valid = withoutLocal.localSize_0 > 0 && withoutLocal.localSize_1 > 0
+            && withoutLocal.localSize_0 * withoutLocal.localSize_1 < MAX_GROUP_SIZE
+            && withoutLocal.globalSize_0 % withoutLocal.localSize_0 == 0
+            && withoutLocal.globalSize_1 % withoutLocal.localSize_1 == 0;
+
+      return (withoutLocal);
+   }
+
+   /** 
+    * Create a two dimensional range <code>0.._globalWidth * 0.._globalHeight *0../_globalDepth</code> 
+    * in groups defined by  <code>localWidth</code> * <code>localHeight</code> * <code>localDepth</code>.
+    * <p>
+    * Note that for this range to be valid  <code>_globalWidth > 0 &&  _globalHeight >0 _globalDepth >0 && _localWidth>0 && _localHeight>0 && _localDepth>0 && _localWidth*_localHeight*_localDepth < MAX_GROUP_SIZE && _globalWidth%_localWidth==0 && _globalHeight%_localHeight==0 && _globalDepth%_localDepth==0</code>.
+    * 
+    * @param _globalWidth the width of the 3D grid we wish to process
+    * @param _globalHieght the height of the 3D grid we wish to process
+    * @param _globalDepth the depth of the 3D grid we wish to process
+    * @param _localWidth the width of the 3D group we wish to process
+    * @param _localHieght the height of the 3D group we wish to process
+    * @param _localDepth the depth of the 3D group we wish to process
+    * @return
+    */
+   public static Range create3D(int _globalWidth, int _globalHeight, int _globalDepth, int _localWidth, int _localHeight,
+         int _localDepth) {
+      Range range = new Range();
+      range.dims = 3;
+      range.globalSize_0 = _globalWidth;
+      range.localSize_0 = _localWidth;
+      range.globalSize_1 = _globalHeight;
+      range.localSize_1 = _localHeight;
+      range.globalSize_2 = _globalDepth;
+      range.localSize_2 = _localDepth;
+      range.valid = range.localSize_0 > 0 && range.localSize_1 > 0 && range.localSize_2 > 0
+            && range.localSize_0 * range.localSize_1 * range.localSize_2 < MAX_GROUP_SIZE
+            && range.globalSize_0 % range.localSize_0 == 0 && range.globalSize_1 % range.localSize_1 == 0
+            && range.globalSize_2 % range.localSize_2 == 0;
+
+      return (range);
+   }
+
+   /** 
+    * Create a two dimensional range <code>0.._globalWidth * 0.._globalHeight *0../_globalDepth</code> 
+    * choosing suitable values for <code>localWidth</code>, <code>localHeight</code> and <code>localDepth</code>.
+    * <p>
+     * Note that for this range to be valid  <code>_globalWidth > 0 &&  _globalHeight >0 _globalDepth >0 && _localWidth>0 && _localHeight>0 && _localDepth>0 && _localWidth*_localHeight*_localDepth < MAX_GROUP_SIZE && _globalWidth%_localWidth==0 && _globalHeight%_localHeight==0 && _globalDepth%_localDepth==0</code>.
+    * 
+    * <p>
+    * To determine suitable values for <code>_localWidth</code>,<code>_localHeight</code> and <code>_lodalDepth</code> we extract the factors for <code>_globalWidth</code>,<code>_globalHeight</code> and <code>_globalDepth</code> and then 
+    * find the largest product ( <code><= MAX_GROUP_SIZE</code>) with the lowest perimeter.
+    * 
+    * <p>
+    * For example for <code>MAX_GROUP_SIZE</code> of 64 we favor 4x4x4 over 1x16x16.
+    * 
+    * @param _globalWidth the width of the 3D grid we wish to process
+    * @param _globalHieght the height of the 3D grid we wish to process
+    * @param _globalDepth the depth of the 3D grid we wish to process
+    * @return
+    */
+   public static Range create3D(int _globalWidth, int _globalHeight, int _globalDepth) {
+      Range withoutLocal = create3D(_globalWidth, _globalHeight, _globalDepth, 1, 1, 1);
+      withoutLocal.localIsDerived = true;
+      int[] widthFactors = getFactors(_globalWidth);
+      int[] heightFactors = getFactors(_globalHeight);
+      int[] depthFactors = getFactors(_globalDepth);
+
+      withoutLocal.localSize_0 = 1;
+      withoutLocal.localSize_1 = 1;
+      withoutLocal.localSize_2 = 1;
+      int max = 1;
+      int perimeter = 0;
+      for (int w : widthFactors) {
+         for (int h : heightFactors) {
+            for (int d : depthFactors) {
+               int size = w * h * d;
+               if (size > MAX_GROUP_SIZE) {
+                  break;
+               }
+               if (size > max) {
+                  max = size;
+                  perimeter = w + h + d;
+                  withoutLocal.localSize_0 = w;
+                  withoutLocal.localSize_1 = h;
+                  withoutLocal.localSize_2 = d;
+               } else if (size == max) {
+                  int localPerimeter = w + h + d;
+                  if (localPerimeter < perimeter) { // is this the shortest perimeter so far
+                     perimeter = localPerimeter;
+                     withoutLocal.localSize_0 = w;
+                     withoutLocal.localSize_1 = h;
+                     withoutLocal.localSize_2 = d;
+                  }
+               }
+            }
+         }
+      }
+
+      withoutLocal.valid = withoutLocal.localSize_0 > 0 && withoutLocal.localSize_1 > 0 && withoutLocal.localSize_2 > 0
+            && withoutLocal.localSize_0 * withoutLocal.localSize_1 * withoutLocal.localSize_2 < MAX_GROUP_SIZE
+            && withoutLocal.globalSize_0 % withoutLocal.localSize_0 == 0
+            && withoutLocal.globalSize_1 % withoutLocal.localSize_1 == 0
+            && withoutLocal.globalSize_2 % withoutLocal.localSize_2 == 0;
+
+      return (withoutLocal);
+
+   }
+
+   /**
+    * Get the number of dims for this Range.  
+    * 
+    * @return 0, 1 or 2 for one dimensional, two dimensional and three dimensional range respectively.
+    */
+   public int getDims() {
+      return (dims);
+   }
+
+   /**
+    * Override {@link #toString()}
+    */
+   public String toString() {
+      StringBuilder sb = new StringBuilder();
+
+      switch (dims) {
+         case 1:
+
+            sb.append("global:" + globalSize_0 + " local:" + (localIsDerived ? "(derived)" : "") + localSize_0);
+
+            break;
+         case 2:
+            sb.append("2D(global:" + globalSize_0 + "x" + globalSize_1 + " local:" + (localIsDerived ? "(derived)" : "")
+                  + localSize_0 + "x" + localSize_1 + ")");
+            break;
+         case 3:
+            sb.append("3D(global:" + globalSize_0 + "x" + globalSize_1 + "x" + globalSize_2 + " local:"
+                  + (localIsDerived ? "(derived)" : "") + localSize_0 + "x" + localSize_1 + "x" + localSize_0 + ")");
+            break;
+
+      }
+      return (sb.toString());
+   }
+
+   /**
+    * Get the number of groups for the given dimension. 
+    * 
+    * <p>
+    * This will essentially return globalXXXX/localXXXX for the given dimension (width, height, depth)
+    * @param _dim The dim we are interested in 0, 1 or 2
+    * @return the number of groups for the given dimension. 
+    */
+
+   public int getNumGroups(int _dim) {
+      return (_dim == 0 ? (globalSize_0 / localSize_0) : (_dim == 1 ? (globalSize_1 / localSize_1) : (globalSize_2 / localSize_2)));
+   }
+
+   /**
+    * 
+    * @return The product of all valid localSize dimensions
+    */
+   public int getWorkGroupSize() {
+      return localSize_0 * localSize_1 * localSize_2;
+   }
+
+   /**
+    * Determine whether this Range is usable. 
+    * 
+    * @return true if this Range is usable/valid. 
+    */
+
+   public boolean isValid() {
+      return (valid);
+   }
+
+}
diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/RangeException.java b/com.amd.aparapi/src/java/com/amd/aparapi/RangeException.java
new file mode 100644
index 00000000..b3998f84
--- /dev/null
+++ b/com.amd.aparapi/src/java/com/amd/aparapi/RangeException.java
@@ -0,0 +1,46 @@
+/*
+Copyright (c) 2010-2011, Advanced Micro Devices, Inc.
+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 the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+If you use the software (in whole or in part), you shall adhere to all applicable U.S., European, and other export
+laws, including but not limited to the U.S. Export Administration Regulations ("EAR"), (15 C.F.R. Sections 730 through
+774), and E.U. Council Regulation (EC) No 1334/2000 of 22 June 2000.  Further, pursuant to Section 740.6 of the EAR,
+you hereby certify that, except pursuant to a license granted by the United States Department of Commerce Bureau of 
+Industry and Security or as otherwise permitted pursuant to a License Exception under the U.S. Export Administration 
+Regulations ("EAR"), you will not (1) export, re-export or release to a national of a country in Country Groups D:1,
+E:1 or E:2 any restricted technology, software, or source code you receive hereunder, or (2) export to Country Groups
+D:1, E:1 or E:2 the direct product of such technology or software, if such foreign produced direct product is subject
+to national security controls as identified on the Commerce Control List (currently found in Supplement 1 to Part 774
+of EAR).  For the most current Country Group listings, or for additional information about the EAR or your obligations
+under those regulations, please refer to the U.S. Bureau of Industry and Security's website at http://www.bis.doc.gov/. 
+
+*/
+package com.amd.aparapi;
+
+@SuppressWarnings("serial") class RangeException extends AparapiException{
+
+   RangeException(String msg) {
+      super(msg);
+   }
+
+}
diff --git a/examples/effects/src/com/amd/aparapi/examples/effects/Main.java b/examples/effects/src/com/amd/aparapi/examples/effects/Main.java
index dab3078f..5543d94c 100644
--- a/examples/effects/src/com/amd/aparapi/examples/effects/Main.java
+++ b/examples/effects/src/com/amd/aparapi/examples/effects/Main.java
@@ -53,6 +53,7 @@ import javax.swing.JComponent;
 import javax.swing.JFrame;
 
 import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
 
 /**
  * An example Aparapi application which tracks the mouse and updates the color pallete of the window based on the distance from the mouse pointer. 
@@ -161,6 +162,8 @@ public class Main{
       /** Height of Mandelbrot view. */
       final int height = 1024;
 
+      final Range range = Range.create2D(width, height);
+
       /** The size of the pallette of pixel colors we choose from. */
       final int palletteSize = 128;
 
@@ -234,7 +237,7 @@ public class Main{
 
       int trailLastUpdatedPosition = 0;
 
-      kernel.execute(width * height);
+      kernel.execute(range);
       System.arraycopy(rgb, 0, imageRgb, 0, rgb.length);
       viewer.repaint();
 
@@ -268,7 +271,7 @@ public class Main{
          trailLastUpdatedPosition++;
 
          /** execute the kernel which calculates new pixel values **/
-         kernel.execute(width * height);
+         kernel.execute(range);
 
          /** copy the rgb values to the imageRgb buffer **/
          System.arraycopy(rgb, 0, imageRgb, 0, rgb.length);
diff --git a/examples/nbody/local.bat b/examples/nbody/local.bat
new file mode 100644
index 00000000..9fe4eb04
--- /dev/null
+++ b/examples/nbody/local.bat
@@ -0,0 +1,14 @@
+@echo off
+
+java ^
+  -Djava.library.path=..\..\com.amd.aparapi.jni;jogamp ^
+  -Dcom.amd.aparapi.executionMode=%1 ^
+  -Dcom.amd.aparapi.enableShowGeneratedOpenCL=true ^
+  -Dcom.amd.aparapi.enableVerboseJNI=false ^
+  -Dbodies=%2 ^
+  -Dheight=600 ^
+  -Dwidth=600 ^
+  -classpath jogamp\gluegen-rt.jar;jogamp\jogl.all.jar;..\..\com.amd.aparapi\aparapi.jar;nbody.jar ^
+  com.amd.aparapi.examples.nbody.Local
+
+
diff --git a/examples/nbody/src/com/amd/aparapi/examples/nbody/Local.java b/examples/nbody/src/com/amd/aparapi/examples/nbody/Local.java
new file mode 100644
index 00000000..7fd272e2
--- /dev/null
+++ b/examples/nbody/src/com/amd/aparapi/examples/nbody/Local.java
@@ -0,0 +1,356 @@
+/*
+Copyright (c) 2010-2011, Advanced Micro Devices, Inc.
+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 the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+If you use the software (in whole or in part), you shall adhere to all applicable U.S., European, and other export
+laws, including but not limited to the U.S. Export Administration Regulations ("EAR"), (15 C.F.R. Sections 730 through
+774), and E.U. Council Regulation (EC) No 1334/2000 of 22 June 2000.  Further, pursuant to Section 740.6 of the EAR,
+you hereby certify that, except pursuant to a license granted by the United States Department of Commerce Bureau of 
+Industry and Security or as otherwise permitted pursuant to a License Exception under the U.S. Export Administration 
+Regulations ("EAR"), you will not (1) export, re-export or release to a national of a country in Country Groups D:1,
+E:1 or E:2 any restricted technology, software, or source code you receive hereunder, or (2) export to Country Groups
+D:1, E:1 or E:2 the direct product of such technology or software, if such foreign produced direct product is subject
+to national security controls as identified on the Commerce Control List (currently found in Supplement 1 to Part 774
+of EAR).  For the most current Country Group listings, or for additional information about the EAR or your obligations
+under those regulations, please refer to the U.S. Bureau of Industry and Security's website at http://www.bis.doc.gov/. 
+
+*/
+package com.amd.aparapi.examples.nbody;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilities;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.GLException;
+import javax.media.opengl.awt.GLCanvas;
+import javax.media.opengl.fixedfunc.GLLightingFunc;
+import javax.media.opengl.glu.GLU;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.WindowConstants;
+
+import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
+import com.jogamp.opengl.util.FPSAnimator;
+import com.jogamp.opengl.util.texture.Texture;
+import com.jogamp.opengl.util.texture.TextureIO;
+
+/**
+ * An NBody clone which uses local memory to cache NBody positions for execution.
+ * 
+ * http://www.browndeertechnology.com/docs/BDT_OpenCL_Tutorial_NBody-rev3.html
+ * 
+ * @see com.amd.aparapi.examples.nbody.Main
+ * 
+ * @author gfrost
+ *
+ */
+public class Local{
+
+   public static class NBodyKernel extends Kernel{
+      protected final float delT = .005f;
+
+      protected final float espSqr = 1.0f;
+
+      protected final float mass = 5f;
+
+      private final Range range;
+
+      private final float[] xyz; // positions xy and z of bodies
+
+      private final float[] vxyz; // velocity component of x,y and z of bodies 
+
+      @Local private final float[] localStuff; // local memory
+
+      /**
+       * Constructor initializes xyz and vxyz arrays.
+       * @param _bodies
+       */
+      public NBodyKernel(Range _range) {
+         range = _range;
+         localStuff = new float[range.getLocalSize(0) * 3];
+
+         xyz = new float[range.getGlobalSize(0) * 3];
+         vxyz = new float[range.getGlobalSize(0) * 3];
+         float maxDist = 20f;
+         for (int body = 0; body < range.getGlobalSize(0) * 3; body += 3) {
+            float theta = (float) (Math.random() * Math.PI * 2);
+            float phi = (float) (Math.random() * Math.PI * 2);
+            float radius = (float) (Math.random() * maxDist);
+
+            // get the 3D dimensional coordinates
+            xyz[body + 0] = (float) (radius * Math.cos(theta) * Math.sin(phi));
+            xyz[body + 1] = (float) (radius * Math.sin(theta) * Math.sin(phi));
+            xyz[body + 2] = (float) (radius * Math.cos(phi));
+
+            // divide into two 'spheres of bodies' by adjusting x 
+            if (body % 2 == 0) {
+               xyz[body + 0] += maxDist * 1.5;
+            } else {
+               xyz[body + 0] -= maxDist * 1.5;
+            }
+         }
+         setExplicit(true);
+      }
+
+      /** 
+       * Here is the kernel entrypoint. Here is where we calculate the position of each body
+       */
+      @Override public void run() {
+
+         int globalId = getGlobalId(0) * 3;
+
+         float accx = 0.f;
+         float accy = 0.f;
+         float accz = 0.f;
+         float myPosx = xyz[globalId + 0];
+         float myPosy = xyz[globalId + 1];
+         float myPosz = xyz[globalId + 2];
+
+         for (int tile = 0; tile < getGlobalSize(0) / getLocalSize(0); tile++) {
+            // load one tile into local memory
+            int gidx = (tile * getLocalSize(0) + getLocalId()) * 3;
+            int lidx = getLocalId(0) * 3;
+            localStuff[lidx + 0] = xyz[gidx + 0];
+            localStuff[lidx + 1] = xyz[gidx + 1];
+            localStuff[lidx + 2] = xyz[gidx + 2];
+            // Synchronize to make sure data is available for processing
+            localBarrier();
+
+            for (int i = 0; i < getLocalSize() * 3; i += 3) {
+               float dx = localStuff[i + 0] - myPosx;
+               float dy = localStuff[i + 1] - myPosy;
+               float dz = localStuff[i + 2] - myPosz;
+               float invDist = rsqrt((dx * dx) + (dy * dy) + (dz * dz) + espSqr);
+               float s = mass * invDist * invDist * invDist;
+               accx = accx + s * dx;
+               accy = accy + s * dy;
+               accz = accz + s * dz;
+            }
+            localBarrier();
+         }
+         accx = accx * delT;
+         accy = accy * delT;
+         accz = accz * delT;
+         xyz[globalId + 0] = myPosx + vxyz[globalId + 0] * delT + accx * .5f * delT;
+         xyz[globalId + 1] = myPosy + vxyz[globalId + 1] * delT + accy * .5f * delT;
+         xyz[globalId + 2] = myPosz + vxyz[globalId + 2] * delT + accz * .5f * delT;
+
+         vxyz[globalId + 0] = vxyz[globalId + 0] + accx;
+         vxyz[globalId + 1] = vxyz[globalId + 1] + accy;
+         vxyz[globalId + 2] = vxyz[globalId + 2] + accz;
+      }
+
+      /**
+       * Render all particles to the OpenGL context
+       * @param gl
+       */
+
+      protected void render(GL2 gl) {
+         gl.glBegin(GL2.GL_QUADS);
+
+         for (int i = 0; i < range.getGlobalSize(0) * 3; i += 3) {
+            gl.glTexCoord2f(0, 1);
+            gl.glVertex3f(xyz[i + 0], xyz[i + 1] + 1, xyz[i + 2]);
+            gl.glTexCoord2f(0, 0);
+            gl.glVertex3f(xyz[i + 0], xyz[i + 1], xyz[i + 2]);
+            gl.glTexCoord2f(1, 0);
+            gl.glVertex3f(xyz[i + 0] + 1, xyz[i + 1], xyz[i + 2]);
+            gl.glTexCoord2f(1, 1);
+            gl.glVertex3f(xyz[i + 0] + 1, xyz[i + 1] + 1, xyz[i + 2]);
+         }
+         gl.glEnd();
+      }
+
+   }
+
+   public static int width;
+
+   public static int height;
+
+   public static boolean running;
+
+   public static void main(String _args[]) {
+
+      final NBodyKernel kernel = new NBodyKernel(Range.create(Integer.getInteger("bodies", 8192), 256));
+
+      JFrame frame = new JFrame("NBody");
+
+      JPanel panel = new JPanel(new BorderLayout());
+      JPanel controlPanel = new JPanel(new FlowLayout());
+      panel.add(controlPanel, BorderLayout.SOUTH);
+
+      final JButton startButton = new JButton("Start");
+
+      startButton.addActionListener(new ActionListener(){
+         @Override public void actionPerformed(ActionEvent e) {
+            running = true;
+            startButton.setEnabled(false);
+         }
+      });
+      controlPanel.add(startButton);
+      controlPanel.add(new JLabel(kernel.getExecutionMode().toString()));
+
+      controlPanel.add(new JLabel("   Particles"));
+      controlPanel.add(new JTextField("" + kernel.range.getGlobalSize(0), 5));
+
+      controlPanel.add(new JLabel("FPS"));
+      final JTextField framesPerSecondTextField = new JTextField("0", 5);
+
+      controlPanel.add(framesPerSecondTextField);
+      controlPanel.add(new JLabel("Score("));
+      JLabel miniLabel = new JLabel("<html><small>calcs</small><hr/><small>&micro;sec</small></html>");
+
+      controlPanel.add(miniLabel);
+      controlPanel.add(new JLabel(")"));
+
+      final JTextField positionUpdatesPerMicroSecondTextField = new JTextField("0", 5);
+
+      controlPanel.add(positionUpdatesPerMicroSecondTextField);
+      GLCapabilities caps = new GLCapabilities(null);
+      caps.setDoubleBuffered(true);
+      caps.setHardwareAccelerated(true);
+      final GLCanvas canvas = new GLCanvas(caps);
+      Dimension dimension = new Dimension(Integer.getInteger("width", 742), Integer.getInteger("height", 742));
+      canvas.setPreferredSize(dimension);
+
+      canvas.addGLEventListener(new GLEventListener(){
+         private double ratio;
+
+         private final float xeye = 0f;
+
+         private final float yeye = 0f;
+
+         private final float zeye = 100f;
+
+         private final float xat = 0f;
+
+         private final float yat = 0f;
+
+         private final float zat = 0f;
+
+         public final float zoomFactor = 1.0f;
+
+         private int frames;
+
+         private long last = System.currentTimeMillis();
+
+         @Override public void dispose(GLAutoDrawable drawable) {
+
+         }
+
+         @Override public void display(GLAutoDrawable drawable) {
+
+            GL2 gl = drawable.getGL().getGL2();
+
+            gl.glLoadIdentity();
+            gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+            gl.glColor3f(1f, 1f, 1f);
+
+            GLU glu = new GLU();
+            glu.gluPerspective(45f, ratio, 0f, 1000f);
+
+            glu.gluLookAt(xeye, yeye, zeye * zoomFactor, xat, yat, zat, 0f, 1f, 0f);
+            if (running) {
+               kernel.execute(kernel.range);
+               if (kernel.isExplicit()) {
+                  kernel.get(kernel.xyz);
+               }
+            }
+            kernel.render(gl);
+
+            long now = System.currentTimeMillis();
+            long time = now - last;
+            frames++;
+
+            if (time > 1000) { // We update the frames/sec every second
+               if (running) {
+                  float framesPerSecond = (frames * 1000.0f) / time;
+                  int updatesPerMicroSecond = (int) ((framesPerSecond * kernel.range.getGlobalSize(0) * kernel.range
+                        .getGlobalSize(0)) / 1000000);
+                  framesPerSecondTextField.setText(String.format("%5.2f", framesPerSecond));
+                  positionUpdatesPerMicroSecondTextField.setText(String.format("%4d", updatesPerMicroSecond));
+               }
+               frames = 0;
+               last = now;
+            }
+            gl.glFlush();
+
+         }
+
+         @Override public void init(GLAutoDrawable drawable) {
+            final GL2 gl = drawable.getGL().getGL2();
+
+            gl.glShadeModel(GLLightingFunc.GL_SMOOTH);
+            gl.glEnable(GL.GL_BLEND);
+            gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
+            try {
+               InputStream textureStream = Local.class.getResourceAsStream("particle.jpg");
+               Texture texture = TextureIO.newTexture(textureStream, false, null);
+               texture.enable(gl);
+            } catch (IOException e) {
+               e.printStackTrace();
+            } catch (GLException e) {
+               e.printStackTrace();
+            }
+
+         }
+
+         @Override public void reshape(GLAutoDrawable drawable, int x, int y, int _width, int _height) {
+            width = _width;
+            height = _height;
+
+            GL2 gl = drawable.getGL().getGL2();
+            gl.glViewport(0, 0, width, height);
+
+            ratio = (double) width / (double) height;
+
+         }
+
+      });
+
+      panel.add(canvas, BorderLayout.CENTER);
+      frame.getContentPane().add(panel, BorderLayout.CENTER);
+
+      frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+      frame.pack();
+      frame.setVisible(true);
+
+      FPSAnimator animator = new FPSAnimator(canvas, 100);
+      animator.start();
+
+   }
+
+}
diff --git a/examples/nbody/src/com/amd/aparapi/examples/nbody/Main.java b/examples/nbody/src/com/amd/aparapi/examples/nbody/Main.java
index ae92a90a..016a7084 100644
--- a/examples/nbody/src/com/amd/aparapi/examples/nbody/Main.java
+++ b/examples/nbody/src/com/amd/aparapi/examples/nbody/Main.java
@@ -62,10 +62,23 @@ import javax.swing.JTextField;
 import javax.swing.WindowConstants;
 
 import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
 import com.jogamp.opengl.util.FPSAnimator;
 import com.jogamp.opengl.util.texture.Texture;
 import com.jogamp.opengl.util.texture.TextureIO;
 
+/**
+ * NBody implementing demonstrating Aparapi kernels. 
+ * 
+ * For a description of the NBody problem. 
+ * @see http://en.wikipedia.org/wiki/N-body_problem
+ * 
+ * We use JOGL to render the bodies. 
+ * @see http://jogamp.org/jogl/www/
+ * 
+ * @author gfrost
+ *
+ */
 public class Main{
 
    public static class NBodyKernel extends Kernel{
@@ -75,7 +88,7 @@ public class Main{
 
       protected final float mass = 5f;
 
-      private final int bodies;
+      private final Range range;
 
       private final float[] xyz; // positions xy and z of bodies
 
@@ -85,27 +98,32 @@ public class Main{
        * Constructor initializes xyz and vxyz arrays.
        * @param _bodies
        */
-      public NBodyKernel(int _bodies) {
-         bodies = _bodies;
-         xyz = new float[bodies * 3];
-         vxyz = new float[bodies * 3];
+      public NBodyKernel(Range _range) {
+         range = _range;
+         // range = Range.create(bodies);
+         xyz = new float[range.getGlobalSize(0) * 3];
+         vxyz = new float[range.getGlobalSize(0) * 3];
          float maxDist = 20f;
-         for (int body = 0; body < bodies * 3; body += 3) {
-            // If I could remmember some basic algebra I guess I could avoid this loop ;) 
-            // just ensures that the x,y,z is within maxdist radius of origin
-            do {
-               xyz[body + 0] = (float) (Math.random() * 2 * maxDist) - maxDist; //x
-               xyz[body + 1] = (float) (Math.random() * 2 * maxDist) - maxDist; //y
-               xyz[body + 2] = (float) (Math.random() * 2 * maxDist) - maxDist; //z
-            } while (xyz[body + 0] * xyz[body + 0] + xyz[body + 1] * xyz[body + 1] + xyz[body + 2] * xyz[body + 2] > maxDist
-                  * maxDist);
-            // divide into two 'sphere of bodies' by adjusting x 
+         for (int body = 0; body < range.getGlobalSize(0) * 3; body += 3) {
+
+            float theta = (float) (Math.random() * Math.PI * 2);
+            float phi = (float) (Math.random() * Math.PI * 2);
+            float radius = (float) (Math.random() * maxDist);
+
+            // get the 3D dimensional coordinates
+            xyz[body + 0] = (float) (radius * Math.cos(theta) * Math.sin(phi));
+            xyz[body + 1] = (float) (radius * Math.sin(theta) * Math.sin(phi));
+            xyz[body + 2] = (float) (radius * Math.cos(phi));
+
+            // divide into two 'spheres of bodies' by adjusting x 
+
             if (body % 2 == 0) {
                xyz[body + 0] += maxDist * 1.5;
             } else {
                xyz[body + 0] -= maxDist * 1.5;
             }
          }
+         setExplicit(true);
       }
 
       /** 
@@ -113,7 +131,7 @@ public class Main{
        */
       @Override public void run() {
          int body = getGlobalId();
-         int count = bodies * 3;
+         int count = getGlobalSize(0) * 3;
          int globalId = body * 3;
 
          float accx = 0.f;
@@ -153,7 +171,7 @@ public class Main{
       protected void render(GL2 gl) {
          gl.glBegin(GL2.GL_QUADS);
 
-         for (int i = 0; i < bodies * 3; i += 3) {
+         for (int i = 0; i < range.getGlobalSize(0) * 3; i += 3) {
             gl.glTexCoord2f(0, 1);
             gl.glVertex3f(xyz[i + 0], xyz[i + 1] + 1, xyz[i + 2]);
             gl.glTexCoord2f(0, 0);
@@ -176,7 +194,7 @@ public class Main{
 
    public static void main(String _args[]) {
 
-      final NBodyKernel kernel = new NBodyKernel(Integer.getInteger("bodies", 8192));
+      final NBodyKernel kernel = new NBodyKernel(Range.create(Integer.getInteger("bodies", 8192)));
 
       JFrame frame = new JFrame("NBody");
 
@@ -196,7 +214,7 @@ public class Main{
       controlPanel.add(new JLabel(kernel.getExecutionMode().toString()));
 
       controlPanel.add(new JLabel("   Particles"));
-      controlPanel.add(new JTextField("" + kernel.bodies, 5));
+      controlPanel.add(new JTextField("" + kernel.range.getGlobalSize(0), 5));
 
       controlPanel.add(new JLabel("FPS"));
       final JTextField framesPerSecondTextField = new JTextField("0", 5);
@@ -256,7 +274,10 @@ public class Main{
 
             glu.gluLookAt(xeye, yeye, zeye * zoomFactor, xat, yat, zat, 0f, 1f, 0f);
             if (running) {
-               kernel.execute(kernel.bodies);
+               kernel.execute(kernel.range);
+               if (kernel.isExplicit()) {
+                  kernel.get(kernel.xyz);
+               }
             }
             kernel.render(gl);
 
@@ -267,7 +288,8 @@ public class Main{
             if (time > 1000) { // We update the frames/sec every second
                if (running) {
                   float framesPerSecond = (frames * 1000.0f) / time;
-                  int updatesPerMicroSecond = (int) ((framesPerSecond * kernel.bodies * kernel.bodies) / 1000000);
+                  int updatesPerMicroSecond = (int) ((framesPerSecond * kernel.range.getGlobalSize(0) * kernel.range
+                        .getGlobalSize(0)) / 1000000);
                   framesPerSecondTextField.setText(String.format("%5.2f", framesPerSecond));
                   positionUpdatesPerMicroSecondTextField.setText(String.format("%4d", updatesPerMicroSecond));
                }
@@ -316,7 +338,7 @@ public class Main{
       frame.pack();
       frame.setVisible(true);
 
-      FPSAnimator animator = new FPSAnimator(canvas, 200);
+      FPSAnimator animator = new FPSAnimator(canvas, 100);
       animator.start();
 
    }
diff --git a/samples/blackscholes/.classpath b/samples/blackscholes/.classpath
new file mode 100644
index 00000000..2b3d4294
--- /dev/null
+++ b/samples/blackscholes/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/com.amd.aparapi"/>
+	<classpathentry kind="output" path="classes"/>
+</classpath>
diff --git a/samples/blackscholes/.project b/samples/blackscholes/.project
new file mode 100644
index 00000000..eb5be55b
--- /dev/null
+++ b/samples/blackscholes/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>blackscholes</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/samples/blackscholes/src/com/amd/aparapi/samples/blackscholes/Main.java b/samples/blackscholes/src/com/amd/aparapi/samples/blackscholes/Main.java
index a80ddd65..60c31142 100644
--- a/samples/blackscholes/src/com/amd/aparapi/samples/blackscholes/Main.java
+++ b/samples/blackscholes/src/com/amd/aparapi/samples/blackscholes/Main.java
@@ -38,6 +38,7 @@ under those regulations, please refer to the U.S. Bureau of Industry and Securit
 package com.amd.aparapi.samples.blackscholes;
 
 import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
 
 public class Main{
 
@@ -181,9 +182,10 @@ public class Main{
    public static void main(String[] _args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
 
       int size = Integer.getInteger("size", 512);
-      int iterations = Integer.getInteger("iterations", 5); 
-      System.out.println("size ="+size);
-      System.out.println("iterations ="+iterations);
+      Range range = Range.create(size);
+      int iterations = Integer.getInteger("iterations", 5);
+      System.out.println("size =" + size);
+      System.out.println("iterations =" + iterations);
       BlackScholesKernel kernel = new BlackScholesKernel(size);
 
       long totalExecTime = 0;
@@ -193,7 +195,7 @@ public class Main{
          iterExecTime = kernel.execute(size).getExecutionTime();
          totalExecTime += iterExecTime;
       }*/
-      kernel.execute(size, iterations);
+      kernel.execute(range, iterations);
       System.out.println("Average execution time " + kernel.getAccumulatedExecutionTime() / iterations);
       kernel.showResults(10);
 
diff --git a/samples/convolution/.classpath b/samples/convolution/.classpath
new file mode 100644
index 00000000..2b3d4294
--- /dev/null
+++ b/samples/convolution/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/com.amd.aparapi"/>
+	<classpathentry kind="output" path="classes"/>
+</classpath>
diff --git a/samples/convolution/.project b/samples/convolution/.project
new file mode 100644
index 00000000..a304e12f
--- /dev/null
+++ b/samples/convolution/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>convolution</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/samples/convolution/build.xml b/samples/convolution/build.xml
new file mode 100644
index 00000000..90979e33
--- /dev/null
+++ b/samples/convolution/build.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+
+<project name="convolution" default="build" basedir=".">
+   <target name="build" depends="clean">
+      <mkdir dir="classes"/>
+      <javac srcdir="src" destdir="classes" debug="on" includeantruntime="false" >
+         <classpath>
+            <pathelement path="../../com.amd.aparapi/aparapi.jar"/>
+         </classpath>
+      </javac>
+      <jar jarfile="${ant.project.name}.jar" basedir="classes"/>
+   </target>
+
+   <target name="clean">
+      <delete dir="classes"/>
+      <delete file="${ant.project.name}.jar"/>
+   </target>
+
+
+</project>
diff --git a/samples/convolution/conv.bat b/samples/convolution/conv.bat
new file mode 100644
index 00000000..ac0c4aab
--- /dev/null
+++ b/samples/convolution/conv.bat
@@ -0,0 +1,6 @@
+java ^
+ -Djava.library.path=../../com.amd.aparapi.jni ^
+ -Dcom.amd.aparapi.executionMode=%1 ^
+ -classpath ../../com.amd.aparapi/aparapi.jar;convolution.jar ^
+ com.amd.aparapi.sample.convolution.Main
+
diff --git a/samples/convolution/src/com/amd/aparapi/sample/convolution/Main.java b/samples/convolution/src/com/amd/aparapi/sample/convolution/Main.java
new file mode 100644
index 00000000..01964181
--- /dev/null
+++ b/samples/convolution/src/com/amd/aparapi/sample/convolution/Main.java
@@ -0,0 +1,234 @@
+/*
+Copyright (c) 2010-2011, Advanced Micro Devices, Inc.
+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 the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+If you use the software (in whole or in part), you shall adhere to all applicable U.S., European, and other export
+laws, including but not limited to the U.S. Export Administration Regulations ("EAR"), (15 C.F.R. Sections 730 through
+774), and E.U. Council Regulation (EC) No 1334/2000 of 22 June 2000.  Further, pursuant to Section 740.6 of the EAR,
+you hereby certify that, except pursuant to a license granted by the United States Department of Commerce Bureau of 
+Industry and Security or as otherwise permitted pursuant to a License Exception under the U.S. Export Administration 
+Regulations ("EAR"), you will not (1) export, re-export or release to a national of a country in Country Groups D:1,
+E:1 or E:2 any restricted technology, software, or source code you receive hereunder, or (2) export to Country Groups
+D:1, E:1 or E:2 the direct product of such technology or software, if such foreign produced direct product is subject
+to national security controls as identified on the Commerce Control List (currently found in Supplement 1 to Part 774
+of EAR).  For the most current Country Group listings, or for additional information about the EAR or your obligations
+under those regulations, please refer to the U.S. Bureau of Industry and Security's website at http://www.bis.doc.gov/. 
+
+*/
+
+package com.amd.aparapi.sample.convolution;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.WindowConstants;
+
+import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
+
+/**
+ * An example Aparapi application which demonstrates image manipulation via convolution filter
+ * 
+ * Converted to use int buffer and some performance tweaks by Gary Frost
+ * http://processing.org/learning/pixels/
+ * 
+ * @author Gary Frost
+ */
+public class Main{
+   // http://docs.gimp.org/en/plug-in-convmatrix.html
+
+   final static class ConvolutionFilter{
+      private float[] weights;
+
+      private int offset;
+
+      ConvolutionFilter(float _nw, float _n, float ne, float _w, float _o, float _e, float _sw, float _s, float _se, int _offset) {
+         weights = new float[] {
+               _nw,
+               _w,
+               ne,
+               _w,
+               _o,
+               _e,
+               _sw,
+               _s,
+               _se
+         };
+         offset = _offset;
+      }
+
+   }
+
+   private static final ConvolutionFilter NONE = new ConvolutionFilter(0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0);
+
+   private static final ConvolutionFilter BLUR = new ConvolutionFilter(1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0);
+
+   private static final ConvolutionFilter EMBOSS = new ConvolutionFilter(-2f, -1f, 0f, -1f, 1f, 1f, 0f, 1f, 2f, 0);
+
+   public static class ConvolutionKernel extends Kernel{
+
+      private final float[] filter = new float[9];
+
+      private final int[] inputData;
+
+      private final int[] outputData;
+
+      private final int width;
+
+      private final int height;
+
+      private int offset;
+
+      public ConvolutionKernel(int _width, int _height, BufferedImage _inputImage, BufferedImage _outputImage) {
+         inputData = ((DataBufferInt) _inputImage.getRaster().getDataBuffer()).getData();
+         outputData = ((DataBufferInt) _outputImage.getRaster().getDataBuffer()).getData();
+         width = _width;
+         height = _height;
+
+         // setExplicit(true); // This gives us a performance boost
+         //  put(inputData); // Because we are using explicit buffer management we must put the imageData array
+      }
+
+      public void run() {
+
+         int x = getGlobalId(0);
+         int y = getGlobalId(1);
+         int lx = getLocalId(0);
+         int ly = getLocalId(1);
+         int w = getGlobalSize(0);
+         int h = getGlobalSize(1);
+         // System.out.println(x+","+y+" "+lx+","+ly+" "+w+","+h);
+         if (x > 1 && x < (w - 1) && y > 1 && y < (h - 1)) {
+
+            int result = 0;
+            // We handle each color separately using rgbshift as an 8 bit mask for red, green, blue
+            for (int rgbShift = 0; rgbShift < 24; rgbShift += 8) { // 0,8,16
+               int channelAccum = 0;
+               float accum = 0;
+
+               for (int count = 0; count < 9; count++) {
+                  int dx = (count % 3) - 1; // 0,1,2 -> -1,0,1
+                  int dy = (count / 3) - 1; // 0,1,2 -> -1,0,1
+
+                  int rgb = (inputData[((y + dy) * w) + (x + dx)]);
+                  int channelValue = ((rgb >> rgbShift) & 0xff);
+                  accum += filter[count];
+                  channelAccum += channelValue * filter[count++];
+
+               }
+               channelAccum /= accum;
+               channelAccum += offset;
+               channelAccum = max(0, min(channelAccum, 0xff));
+               result |= (channelAccum << rgbShift);
+            }
+            outputData[y * w + x] = result;
+         }
+      }
+
+      public void convolve(ConvolutionFilter _filter) {
+         System.arraycopy(_filter.weights, 0, filter, 0, _filter.weights.length);
+         offset = _filter.offset;
+         put(filter);
+         execute(Range.create2D(width, height, 8, 8));
+         get(outputData);
+      }
+   }
+
+   public static final int PAD = 1024;
+
+   public static int padValue(int value) {
+      return (PAD - (value % PAD));
+   }
+
+   public static int padTo(int value) {
+      return (value + padValue(value));
+   }
+
+   public static void main(String[] _args) throws IOException, InterruptedException {
+
+      JFrame frame = new JFrame("Convolution");
+
+      BufferedImage testCard = ImageIO.read(new File("testcard.jpg"));
+
+      int imageHeight = testCard.getHeight();
+
+      int imageWidth = testCard.getWidth();
+
+      final int width = padTo(imageWidth);// now multiple of 64
+
+      final int height = padTo(imageHeight); // now multiple of 64
+
+      System.out.println("image width,height=" + width + "," + height);
+
+      final BufferedImage inputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+
+      inputImage.getGraphics().drawImage(testCard, padValue(imageWidth) / 2, padValue(imageHeight) / 2, null);
+      final BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+      outputImage.getGraphics().drawImage(testCard, padValue(imageWidth) / 2, padValue(imageHeight) / 2, null);
+      final ConvolutionKernel lifeKernel = new ConvolutionKernel(width, height, inputImage, outputImage);
+
+      // Create a component for viewing the offsecreen image
+      @SuppressWarnings("serial") JComponent viewer = new JComponent(){
+         @Override public void paintComponent(Graphics g) {
+            //  if (lifeKernel.isExplicit()) {
+            //    lifeKernel.get(lifeKernel.inputData); // We only pull the imageData when we intend to use it.
+            //  }
+            g.drawImage(outputImage, 0, 0, width, height, 0, 0, width, height, this);
+         }
+      };
+
+      // Set the default size and add to the frames content pane
+      viewer.setPreferredSize(new Dimension(width, height));
+      frame.getContentPane().add(viewer);
+
+      // Swing housekeeping
+      frame.pack();
+      frame.setVisible(true);
+      frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+      ConvolutionFilter filters[] = new ConvolutionFilter[] {
+            NONE,
+            BLUR,
+            EMBOSS,
+      };
+      long start = System.nanoTime();
+      for (int i = 0; i < 100; i++) {
+         for (ConvolutionFilter filter : filters) {
+
+            lifeKernel.convolve(filter); // Work is performed here
+
+            viewer.repaint(); // Request a repaint of the viewer (causes paintComponent(Graphics) to be called later not synchronous
+            //Thread.sleep(1000);
+         }
+      }
+      System.out.println((System.nanoTime() - start) / 1000000);
+
+   }
+}
diff --git a/samples/convolution/src/com/amd/aparapi/sample/convolution/Test12x4_4x2.java b/samples/convolution/src/com/amd/aparapi/sample/convolution/Test12x4_4x2.java
new file mode 100644
index 00000000..e68ca82c
--- /dev/null
+++ b/samples/convolution/src/com/amd/aparapi/sample/convolution/Test12x4_4x2.java
@@ -0,0 +1,496 @@
+package com.amd.aparapi.sample.convolution;
+
+import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
+
+public class Test12x4_4x2{
+   public static void main(String[] _args) {
+      // globalThreadId, threadId, globalX, globalY, localX, localY
+      final int[][] test = new int[][] {
+            {
+                  0, //globalThreadId
+                  0,//threadId
+                  0,//globalX
+                  0,//globalY
+                  0,//localX
+                  0
+            //localY
+            },
+            {
+                  1,//globalThreadId
+                  1,//threadId
+                  1,//globalX
+                  0,//globalY
+                  1,//localX
+                  0
+            //localY
+            },
+            {
+                  2,//globalThreadId
+                  2,//threadId
+                  2,//globalX
+                  0,//globalY
+                  2,//localX
+                  0
+            //localY
+            },
+            {
+                  3,//globalThreadId
+                  3,//threadId
+                  3,//globalX
+                  0,//globalY
+                  3,//localX
+                  0
+            //localY
+            },
+            {
+                  4,//globalThreadId
+                  4,//threadId
+                  0,//globalX
+                  1,//globalY
+                  0,//localX
+                  1
+            //localY
+            },
+            {
+                  5,//globalThreadId
+                  5,//threadId
+                  1,//globalX
+                  1,//globalY
+                  1,//localX
+                  1
+            //localY
+            },
+            {
+                  6,//globalThreadId
+                  6,//threadId
+                  2,//globalX
+                  1,//globalY
+                  2,//localX
+                  1
+            //localY
+            },
+            {
+                  7,//globalThreadId
+                  7,//threadId
+                  3,//globalX
+                  1,//globalY
+                  3,//localX
+                  1
+            //localY
+            },
+            {
+                  8,//globalThreadId
+                  0,//threadId
+                  4,//globalX
+                  0,//globalY
+                  0,//localX
+                  0
+            //localY
+            },
+            {
+                  9,//globalThreadId
+                  1,//threadId
+                  5,//globalX
+                  0,//globalY
+                  1,//localX
+                  0
+            //localY
+            },
+            {
+                  10,//globalThreadId
+                  2,//threadId
+                  6,//globalX
+                  0,//globalY
+                  2,//localX
+                  0
+            //localY
+            },
+            {
+                  11,//globalThreadId
+                  3,//threadId
+                  7,//globalX
+                  0,//globalY
+                  3,//localX
+                  0
+            //localY
+            },
+            {
+                  12,//globalThreadId
+                  4,//threadId
+                  4,//globalX
+                  1,//globalY
+                  0,//localX
+                  1
+            //localY
+            },
+            {
+                  13,//globalThreadId
+                  5,//threadId
+                  5,//globalX
+                  1,//globalY
+                  1,//localX
+                  1
+            //localY
+            },
+            {
+                  14,//globalThreadId
+                  6,//threadId
+                  6,//globalX
+                  1,//globalY
+                  2,//localX
+                  1
+            //localY
+            },
+            {
+                  15,//globalThreadId
+                  7,//threadId
+                  7,//globalX
+                  1,//globalY
+                  3,//localX
+                  1
+            //localY
+            },
+            {
+                  16,//globalThreadId
+                  0,//threadId
+                  8,//globalX
+                  0,//globalY
+                  0,//localX
+                  0
+            //localY
+            },
+            {
+                  17,//globalThreadId
+                  1,//threadId
+                  9,//globalX
+                  0,//globalY
+                  1,//localX
+                  0
+            //localY
+            },
+            {
+                  18,//globalThreadId
+                  2,//threadId
+                  10,//globalX
+                  0,//globalY
+                  2,//localX
+                  0
+            //localY
+            },
+            {
+                  19,//globalThreadId
+                  3,//threadId
+                  11,//globalX
+                  0,//globalY
+                  3,//localX
+                  0
+            //localY
+            },
+
+            {
+                  20,//globalThreadId
+                  4,//threadId
+                  8,//globalX
+                  1,//globalY
+                  0,//localX
+                  1
+            //localY
+            },
+            {
+                  21,//globalThreadId
+                  5,//threadId
+                  9,//globalX
+                  1,//globalY
+                  1,//localX
+                  1
+            //localY
+            },
+            {
+                  22,//globalThreadId
+                  6,//threadId
+                  10,//globalX
+                  1,
+                  2,//localX
+                  1
+            //localY
+            },
+            {
+                  23,//globalThreadId
+                  7,//threadId
+                  11,//globalX
+                  1,//globalY
+                  3,//localX
+                  1
+            //localY
+            },
+            {
+                  24,//globalThreadId
+                  0,//threadId
+                  0,//globalX
+                  2,//globalY
+                  0,//localX
+                  0
+            //localY
+            },
+            {
+                  25,//globalThreadId
+                  1,//threadId
+                  1,//globalX
+                  2,//globalY
+                  1,//localX
+                  0
+            //localY
+            },
+            {
+                  26,//globalThreadId
+                  2,//threadId
+                  2,//globalX
+                  2,//globalY
+                  2,//localX
+                  0
+            //localY
+            },
+            {
+                  27,//globalThreadId
+                  3,//threadId
+                  3,//globalX
+                  2,//globalY
+                  3,//localX
+                  0
+            //localY
+            },
+            {
+                  28,//globalThreadId
+                  4,//threadId
+                  0,//globalX
+                  3,//globalY
+                  0,//localX
+                  1
+            //localY
+            },
+            {
+                  29,//globalThreadId
+                  5,//threadId
+                  1,//globalX
+                  3,//globalY
+                  1,//localX
+                  1
+            //localY
+            },
+            {
+                  30,//globalThreadId
+                  6,//threadId
+                  2,//globalX
+                  3,//globalY
+                  2,//localX
+                  1
+            //localY
+            },
+            {
+                  31,//globalThreadId
+                  7,//threadId
+                  3,//globalX
+                  3,//globalY
+                  3,//localX
+                  1
+            //localY
+            },
+            {
+                  32,//globalThreadId
+                  0,//threadId
+                  4,//globalX
+                  2,//globalY
+                  0,//localX
+                  0
+            //localY
+            },
+            {
+                  33,//globalThreadId
+                  1,//threadId
+                  5,//globalX
+                  2,//globalY
+                  1,//localX
+                  0
+            //localY
+            },
+            {
+                  34,//globalThreadId
+                  2,//threadId
+                  6,//globalX
+                  2,//globalY
+                  2,//localX
+                  0
+            //localY
+            },
+            {
+                  35,//globalThreadId
+                  3,//threadId
+                  7,//globalX
+                  2,//globalY
+                  3,//localX
+                  0
+            //localY
+            },
+            {
+                  36,//globalThreadId
+                  4,//threadId
+                  4,//globalX
+                  3,//globalY
+                  0,//localX
+                  1
+            //localY
+            },
+            {
+                  37,//globalThreadId
+                  5,//threadId
+                  5,//globalX
+                  3,//globalY
+                  1,//localX
+                  1
+            //localY
+            },
+            {
+                  38,//globalThreadId
+                  6,//threadId
+                  6,//globalX
+                  3,//globalY
+                  2,//localX
+                  1
+            //localY
+            },
+            {
+                  39,//globalThreadId
+                  7,//threadId
+                  7,//globalX
+                  3,//globalY
+                  3,//localX
+                  1
+            //localY
+            },
+            {
+                  40,//globalThreadId
+                  0,//threadId
+                  8,//globalX
+                  2,//globalY
+                  0,//localX
+                  0
+            //localY
+            },
+            {
+                  41,//globalThreadId
+                  1,//threadId
+                  9,//globalX
+                  2,//globalY
+                  1,//localX
+                  0
+            //localY
+            },
+            {
+                  42,//globalThreadId
+                  2,//threadId
+                  10,//globalX
+                  2,//globalY
+                  2,//localX
+                  0
+            //localY
+            },
+            {
+                  43,//globalThreadId
+                  3,//threadId
+                  11,//globalX
+                  2,//globalY
+                  3,//localX
+                  0
+            //localY
+            },
+
+            {
+                  44,//globalThreadId
+                  4,//threadId
+                  8,//globalX
+                  3,//globalY
+                  0,//localX
+                  1
+            //localY
+            },
+            {
+                  45,//globalThreadId
+                  5,//threadId
+                  9,//globalX
+                  3,//globalY
+                  1,//localX
+                  1
+            //localY
+            },
+            {
+                  46,//globalThreadId
+                  6,//threadId
+                  10,//globalX
+                  3,//globalY
+                  2,//localX
+                  1
+            //localY
+            },
+            {
+                  47,//globalThreadId
+                  7,//threadId
+                  11,//globalX
+                  3,//globalY
+                  3,//localX
+                  1
+            //localY
+            },
+      };
+      Kernel kernel = new Kernel(){
+
+         @Override public void run() {
+            int x = getGlobalId(0);
+            int y = getGlobalId(1);
+            int lx = getLocalId(0);
+            int ly = getLocalId(1);
+            int w = getGlobalSize(0);
+            int h = getGlobalSize(1);
+            int globalThreadId = getGlobalId(1) * getGlobalSize(0) + getGlobalId(0);
+            int threadId = getLocalId(1) * getLocalSize(0) + getLocalId(0);
+            synchronized (test) {
+               boolean show = false;
+               if (globalThreadId != test[globalThreadId][0]) {
+                  System.out.println("bad globalThreadId");
+                  show = true;
+               }
+               if (threadId != test[globalThreadId][1]) {
+                  System.out.println("bad threadId");
+                  show = true;
+               }
+               if (x != test[globalThreadId][2]) {
+                  System.out.println("bad globalx");
+                  show = true;
+               }
+               if (y != test[globalThreadId][3]) {
+                  System.out.println("bad globaly");
+                  show = true;
+               }
+               if (lx != test[globalThreadId][4]) {
+                  System.out.println("bad localx");
+                  show = true;
+               }
+               if (ly != test[globalThreadId][5]) {
+                  System.out.println("bad localy");
+                  show = true;
+               }
+               if (show) {
+                  System.out.println("derived =>" + globalThreadId + " " + threadId + " " + x + "," + y + " " + lx + "," + ly + " "
+                        + w + "," + h);
+                  System.out.println("data    =>" + test[globalThreadId][0] + " " + test[globalThreadId][1] + " "
+                        + test[globalThreadId][2] + "," + test[globalThreadId][3] + " " + test[globalThreadId][4] + ","
+                        + test[globalThreadId][5] + " " + w + "," + h);
+               }
+            }
+         }
+
+      };
+      kernel.execute(Range.create2D(12, 4, 4, 2));
+
+   }
+}
diff --git a/samples/convolution/testcard.jpg b/samples/convolution/testcard.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..16b1a709365da51b2247255c903c7c6bb173207f
GIT binary patch
literal 64477
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
G)BgbdQW$yw

literal 0
HcmV?d00001

diff --git a/samples/life/src/com/amd/aparapi/sample/life/Main.java b/samples/life/src/com/amd/aparapi/sample/life/Main.java
index 4ede78b1..73a0a867 100644
--- a/samples/life/src/com/amd/aparapi/sample/life/Main.java
+++ b/samples/life/src/com/amd/aparapi/sample/life/Main.java
@@ -55,6 +55,7 @@ import javax.swing.JPanel;
 import javax.swing.WindowConstants;
 
 import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
 
 /**
  * An example Aparapi application which demonstrates Conways 'Game Of Life'.
@@ -101,6 +102,8 @@ public class Main{
 
       private final int height;
 
+      private final Range range;
+
       private int fromBase;
 
       private int toBase;
@@ -109,6 +112,8 @@ public class Main{
          imageData = ((DataBufferInt) _image.getRaster().getDataBuffer()).getData();
          width = _width;
          height = _height;
+         range = Range.create(width * height, 256);
+         System.out.println("range = " + range);
          fromBase = height * width;
          toBase = 0;
          setExplicit(true); // This gives us a performance boost
@@ -160,7 +165,7 @@ public class Main{
          fromBase = toBase;
          toBase = swap;
 
-         execute(width * height);
+         execute(range);
       }
 
    }
diff --git a/samples/mandel/mandel2D.bat b/samples/mandel/mandel2D.bat
new file mode 100644
index 00000000..93e67e07
--- /dev/null
+++ b/samples/mandel/mandel2D.bat
@@ -0,0 +1,7 @@
+java ^
+ -Djava.library.path=../../com.amd.aparapi.jni ^
+ -Dcom.amd.aparapi.executionMode=%1 ^
+ -classpath ../../com.amd.aparapi/aparapi.jar;mandel.jar ^
+ com.amd.aparapi.sample.mandel.Main2D
+
+
diff --git a/samples/mandel/mandel2D.sh b/samples/mandel/mandel2D.sh
new file mode 100644
index 00000000..99a1c45c
--- /dev/null
+++ b/samples/mandel/mandel2D.sh
@@ -0,0 +1,5 @@
+java\
+ -Djava.library.path=../../com.amd.aparapi.jni\
+ -Dcom.amd.aparapi.executionMode=$1\
+ -classpath ../../com.amd.aparapi/aparapi.jar:mandel.jar\
+ com.amd.aparapi.sample.mandel.Main2D
diff --git a/samples/mandel/src/com/amd/aparapi/sample/mandel/Main.java b/samples/mandel/src/com/amd/aparapi/sample/mandel/Main.java
index 5879f785..99d3373c 100644
--- a/samples/mandel/src/com/amd/aparapi/sample/mandel/Main.java
+++ b/samples/mandel/src/com/amd/aparapi/sample/mandel/Main.java
@@ -53,6 +53,7 @@ import javax.swing.JComponent;
 import javax.swing.JFrame;
 
 import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
 
 /**
  * An example Aparapi application which displays a view of the Mandelbrot set and lets the user zoom in to a particular point. 
@@ -164,6 +165,9 @@ public class Main{
       /** Height of Mandelbrot view. */
       final int height = 768;
 
+      /** Mandelbrot image height. */
+      final Range range = Range.create(width * height);
+
       /** Maximum iterations for Mandelbrot. */
       final int maxIterations = 256;
 
@@ -220,7 +224,7 @@ public class Main{
 
       // Set the default scale and offset, execute the kernel and force a repaint of the viewer.
       kernel.setScaleAndOffset(defaultScale, -1f, 0f);
-      kernel.execute(width * height);
+      kernel.execute(range);
       System.arraycopy(rgb, 0, imageRgb, 0, rgb.length);
       viewer.repaint();
 
@@ -266,7 +270,7 @@ public class Main{
 
                // Set the scale and offset, execute the kernel and force a repaint of the viewer.
                kernel.setScaleAndOffset(scale, x, y);
-               kernel.execute(width * height);
+               kernel.execute(range);
                System.arraycopy(rgb, 0, imageRgb, 0, rgb.length);
                viewer.repaint();
             }
diff --git a/samples/mandel/src/com/amd/aparapi/sample/mandel/Main2D.java b/samples/mandel/src/com/amd/aparapi/sample/mandel/Main2D.java
new file mode 100644
index 00000000..5e6b85a3
--- /dev/null
+++ b/samples/mandel/src/com/amd/aparapi/sample/mandel/Main2D.java
@@ -0,0 +1,277 @@
+/*
+Copyright (c) 2010-2011, Advanced Micro Devices, Inc.
+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 the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+If you use the software (in whole or in part), you shall adhere to all applicable U.S., European, and other export
+laws, including but not limited to the U.S. Export Administration Regulations ("EAR"), (15 C.F.R. Sections 730 through
+774), and E.U. Council Regulation (EC) No 1334/2000 of 22 June 2000.  Further, pursuant to Section 740.6 of the EAR,
+you hereby certify that, except pursuant to a license granted by the United States Department of Commerce Bureau of 
+Industry and Security or as otherwise permitted pursuant to a License Exception under the U.S. Export Administration 
+Regulations ("EAR"), you will not (1) export, re-export or release to a national of a country in Country Groups D:1,
+E:1 or E:2 any restricted technology, software, or source code you receive hereunder, or (2) export to Country Groups
+D:1, E:1 or E:2 the direct product of such technology or software, if such foreign produced direct product is subject
+to national security controls as identified on the Commerce Control List (currently found in Supplement 1 to Part 774
+of EAR).  For the most current Country Group listings, or for additional information about the EAR or your obligations
+under those regulations, please refer to the U.S. Bureau of Industry and Security's website at http://www.bis.doc.gov/. 
+
+*/
+
+package com.amd.aparapi.sample.mandel;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+
+import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
+
+/**
+ * An example Aparapi application which displays a view of the Mandelbrot set and lets the user zoom in to a particular point. 
+ * 
+ * When the user clicks on the view, this example application will zoom in to the clicked point and zoom out there after.
+ * On GPU, additional computing units will offer a better viewing experience. On the other hand on CPU, this example 
+ * application might suffer with sub-optimal frame refresh rate as compared to GPU. 
+ *  
+ * @author gfrost
+ *
+ */
+
+public class Main2D{
+
+   /**
+    * An Aparapi Kernel implementation for creating a scaled view of the mandelbrot set.
+    *  
+    * @author gfrost
+    *
+    */
+
+   public static class MandelKernel extends Kernel{
+
+      /** RGB buffer used to store the Mandelbrot image. This buffer holds (width * height) RGB values. */
+      final private int rgb[];
+
+      /** Palette used for each iteration value 0..maxIterations. */
+      final private int pallette[];
+
+      /** Maximum iterations we will check for. */
+      final private int maxIterations;
+
+      /** Mutable values of scale, offsetx and offsety so that we can modify the zoom level and position of a view. */
+      private float scale = .0f;
+
+      private float offsetx = .0f;
+
+      private float offsety = .0f;
+
+      /**
+       * Initialize the Kernel.
+       *  
+       * @param _width Mandelbrot image width
+       * @param _height Mandelbrot image height
+       * @param _rgb Mandelbrot image RGB buffer
+       * @param _pallette Mandelbrot image palette
+       */
+      public MandelKernel(int[] _rgb, int[] _pallette) {
+
+         rgb = _rgb;
+         pallette = _pallette;
+         maxIterations = pallette.length - 1;
+
+      }
+
+      @Override public void run() {
+
+         /** Determine which RGB value we are going to process (0..RGB.length). */
+         int gid = getGlobalId(1) * getGlobalSize(0) + getGlobalId(0);
+
+         /** Translate the gid into an x an y value. */
+         float x = (((getGlobalId(0) * scale) - ((scale / 2) * getGlobalSize(0))) / getGlobalSize(0)) + offsetx;
+
+         float y = (((getGlobalId(1) * scale) - ((scale / 2) * getGlobalSize(1))) / getGlobalSize(1)) + offsety;
+
+         int count = 0;
+
+         float zx = x;
+         float zy = y;
+         float new_zx = 0f;
+
+         // Iterate until the algorithm converges or until maxIterations are reached.
+         while (count < maxIterations && zx * zx + zy * zy < 8) {
+            new_zx = zx * zx - zy * zy + x;
+            zy = 2 * zx * zy + y;
+            zx = new_zx;
+            count++;
+         }
+
+         // Pull the value out of the palette for this iteration count.
+         rgb[gid] = pallette[count];
+      }
+
+      public void setScaleAndOffset(float _scale, float _offsetx, float _offsety) {
+         offsetx = _offsetx;
+         offsety = _offsety;
+         scale = _scale;
+      }
+
+   }
+
+   /** User selected zoom-in point on the Mandelbrot view. */
+   public static volatile Point to = null;
+
+   @SuppressWarnings("serial") public static void main(String[] _args) {
+
+      JFrame frame = new JFrame("MandelBrot");
+
+      /** Mandelbrot image height. */
+      final Range range = Range.create2D(768, 768);
+      System.out.println("range= " + range);
+
+      /** Maximum iterations for Mandelbrot. */
+      final int maxIterations = 256;
+
+      /** Palette which maps iteration values to RGB values. */
+      final int pallette[] = new int[maxIterations + 1];
+
+      //Initialize palette values
+      for (int i = 0; i < maxIterations; i++) {
+         float h = i / (float) maxIterations;
+         float b = 1.0f - h * h;
+         pallette[i] = Color.HSBtoRGB(h, 1f, b);
+      }
+
+      /** Image for Mandelbrot view. */
+      final BufferedImage image = new BufferedImage(range.getGlobalSize(0), range.getGlobalSize(1), BufferedImage.TYPE_INT_RGB);
+      final BufferedImage offscreen = new BufferedImage(range.getGlobalSize(0), range.getGlobalSize(1), BufferedImage.TYPE_INT_RGB);
+      // Draw Mandelbrot image
+      JComponent viewer = new JComponent(){
+         @Override public void paintComponent(Graphics g) {
+
+            g.drawImage(image, 0, 0, range.getGlobalSize(0), range.getGlobalSize(1), this);
+         }
+      };
+
+      // Set the size of JComponent which displays Mandelbrot image
+      viewer.setPreferredSize(new Dimension(range.getGlobalSize(0), range.getGlobalSize(1)));
+
+      final Object doorBell = new Object();
+
+      // Mouse listener which reads the user clicked zoom-in point on the Mandelbrot view 
+      viewer.addMouseListener(new MouseAdapter(){
+         @Override public void mouseClicked(MouseEvent e) {
+            to = e.getPoint();
+            synchronized (doorBell) {
+               doorBell.notify();
+            }
+         }
+      });
+
+      // Swing housework to create the frame
+      frame.getContentPane().add(viewer);
+      frame.pack();
+      frame.setLocationRelativeTo(null);
+      frame.setVisible(true);
+
+      // Extract the underlying RGB buffer from the image.
+      // Pass this to the kernel so it operates directly on the RGB buffer of the image
+      final int[] rgb = ((DataBufferInt) offscreen.getRaster().getDataBuffer()).getData();
+      final int[] imageRgb = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+      // Create a Kernel passing the size, RGB buffer and the palette.
+      final MandelKernel kernel = new MandelKernel(rgb, pallette);
+
+      float defaultScale = 3f;
+
+      // Set the default scale and offset, execute the kernel and force a repaint of the viewer.
+      kernel.setScaleAndOffset(defaultScale, -1f, 0f);
+      kernel.execute(range);
+      System.arraycopy(rgb, 0, imageRgb, 0, rgb.length);
+      viewer.repaint();
+
+      // Report target execution mode: GPU or JTP (Java Thread Pool).
+      System.out.println("Execution mode=" + kernel.getExecutionMode());
+
+      // Window listener to dispose Kernel resources on user exit.
+      frame.addWindowListener(new WindowAdapter(){
+         public void windowClosing(WindowEvent _windowEvent) {
+            kernel.dispose();
+            System.exit(0);
+         }
+      });
+
+      // Wait until the user selects a zoom-in point on the Mandelbrot view.
+      while (true) {
+
+         // Wait for the user to click somewhere
+         while (to == null) {
+            synchronized (doorBell) {
+               try {
+                  doorBell.wait();
+               } catch (InterruptedException ie) {
+                  ie.getStackTrace();
+               }
+            }
+         }
+
+         float x = -1f;
+         float y = 0f;
+         float scale = defaultScale;
+         float tox = (float) (to.x - range.getGlobalSize(0) / 2) / range.getGlobalSize(0) * scale;
+         float toy = (float) (to.y - range.getGlobalSize(1) / 2) / range.getGlobalSize(1) * scale;
+
+         // This is how many frames we will display as we zoom in and out.
+         int frames = 128;
+         long startMillis = System.currentTimeMillis();
+         for (int sign = -1; sign < 2; sign += 2) {
+            for (int i = 0; i < frames - 4; i++) {
+               scale = scale + sign * defaultScale / frames;
+               x = x - sign * (tox / frames);
+               y = y - sign * (toy / frames);
+
+               // Set the scale and offset, execute the kernel and force a repaint of the viewer.
+               kernel.setScaleAndOffset(scale, x, y);
+               kernel.execute(range);
+               System.arraycopy(rgb, 0, imageRgb, 0, rgb.length);
+               viewer.repaint();
+            }
+         }
+
+         long elapsedMillis = System.currentTimeMillis() - startMillis;
+         System.out.println("FPS = " + frames * 1000 / elapsedMillis);
+
+         // Reset zoom-in point.
+         to = null;
+
+      }
+
+   }
+
+}
diff --git a/samples/squares/src/com/amd/aparapi/sample/squares/Main.java b/samples/squares/src/com/amd/aparapi/sample/squares/Main.java
index 929d02b3..32a1b70b 100644
--- a/samples/squares/src/com/amd/aparapi/sample/squares/Main.java
+++ b/samples/squares/src/com/amd/aparapi/sample/squares/Main.java
@@ -39,6 +39,7 @@ under those regulations, please refer to the U.S. Bureau of Industry and Securit
 package com.amd.aparapi.sample.squares;
 
 import com.amd.aparapi.Kernel;
+import com.amd.aparapi.Range;
 
 /**
  * An example Aparapi application which computes and displays squares of a set of 512 input values.
@@ -77,7 +78,8 @@ public class Main{
       };
 
       // Execute Kernel.
-      kernel.execute(512);
+
+      kernel.execute(Range.create(512));
 
       // Report target execution mode: GPU or JTP (Java Thread Pool).
       System.out.println("Execution mode=" + kernel.getExecutionMode());
diff --git a/test/codegen/build.xml b/test/codegen/build.xml
index 1118e3da..5af3d17a 100644
--- a/test/codegen/build.xml
+++ b/test/codegen/build.xml
@@ -2,8 +2,7 @@
 
 <project name="codegen" default="junit" basedir=".">
 
-   <!--<property name="junit.jar" value="/home/gfrost/aparapi/trunk/tools/junit/junit.jar"/>-->
-   <property name="junit.jar" value="C:\Users\gfrost\javalabs\projects\aparapi\trunk\tools\junit\junit.jar"/>
+   <property name="junit.jar" value="./junit-4.10.jar"/>
 
    <path id="classpath">
       <pathelement path="..\..\com.amd.aparapi\aparapi.jar"/>
@@ -11,28 +10,22 @@
       <pathelement path="classes"/>
    </path>
 
-   <target name="check">
-      <fail message="Error:">
-         <condition>
-            <not><isset property="junit.jar"/></not>
-         </condition>
-         <![CDATA[
-         You will need to edit test/codegen/build.xml
-
-         At present junit.dir is not set.  It needs to point to the junit jar file in your junit installation.
+   <target name="check-junit">
+      <condition property="need.to.upload.junit">
+         <not><available file="${junit.jar}"/> </not>
+      </condition>
+   </target>
 
-         You can install/download junit from www.junit.org.
-         ]]>
-      </fail>
-      <available file="${junit.jar}" type="file" property="junit.jar.exists"/>
+   <target name="check" if="need.to.upload.junit">
+      <get dest=".">
+         <url url="http://repo1.maven.org/maven2/junit/junit/4.10/junit-4.10.jar"/>
+      </get>
       <fail message="Error:">
          <condition>
-            <not><isset property="junit.jar.exists"/></not>
+            <not><available file="${junit.jar}"/> </not>
          </condition>
          <![CDATA[
-         You will need to edit test/codegen/build.xml
-
-         At present junit.jar is set to ${junit.jar} but that file does not exist
+         Failed to upload junit from maven repository. 
          ]]>
       </fail>
    </target>
@@ -43,7 +36,7 @@
       <delete dir="src/genjava/com"/>
    </target>
 
-   <target name="junit" depends="clean, check">
+   <target name="junit" depends="clean, check-junit, check">
       <mkdir dir="classes"/>
       <javac srcdir="src/java" destdir="classes" debug="on"  includeAntRuntime="false" classpathref="classpath" />
 
-- 
GitLab