Merge "Reformat and remove STL reference from header"
diff --git a/cpp/rsDispatch.cpp b/cpp/rsDispatch.cpp
index db526fa..e0d6788 100644
--- a/cpp/rsDispatch.cpp
+++ b/cpp/rsDispatch.cpp
@@ -21,7 +21,7 @@
 
 #define LOG_API(...)
 
-bool loadSymbols(void* handle, dispatchTable& dispatchTab) {
+bool loadSymbols(void* handle, dispatchTable& dispatchTab, int device_api) {
     //fucntion to set the native lib path for 64bit compat lib.
 #ifdef __LP64__
     dispatchTab.SetNativeLibDir = (SetNativeLibDirFnPtr)dlsym(handle, "rsaContextSetNativeLibDir");
@@ -320,11 +320,6 @@
         LOG_API("Couldn't initialize dispatchTab.ScriptKernelIDCreate");
         return false;
     }
-    dispatchTab.ScriptInvokeIDCreate = (ScriptInvokeIDCreateFnPtr)dlsym(handle, "rsScriptInvokeIDCreate");
-    if (dispatchTab.ScriptInvokeIDCreate == NULL) {
-        LOG_API("Couldn't initialize dispatchTab.ScriptInvokeIDCreate");
-        return false;
-    }
     dispatchTab.ScriptFieldIDCreate = (ScriptFieldIDCreateFnPtr)dlsym(handle, "rsScriptFieldIDCreate");
     if (dispatchTab.ScriptFieldIDCreate == NULL) {
         LOG_API("Couldn't initialize dispatchTab.ScriptFieldIDCreate");
@@ -366,7 +361,38 @@
         return false;
     }
 
+    // API_23 functions
+    if (device_api >= 23) {
+        //ScriptGroup V2 functions
+        dispatchTab.ScriptInvokeIDCreate = (ScriptInvokeIDCreateFnPtr)dlsym(handle, "rsScriptInvokeIDCreate");
+        if (dispatchTab.ScriptInvokeIDCreate == NULL) {
+            LOG_API("Couldn't initialize dispatchTab.ScriptInvokeIDCreate");
+            return false;
+        }
+        dispatchTab.ClosureCreate = (ClosureCreateFnPtr)dlsym(handle, "rsClosureCreate");
+        if (dispatchTab.ClosureCreate == NULL) {
+            LOG_API("Couldn't initialize dispatchTab.ClosureCreate");
+            return false;
+        }
+        dispatchTab.ClosureSetArg = (ClosureSetArgFnPtr)dlsym(handle, "rsClosureSetArg");
+        if (dispatchTab.ClosureSetArg == NULL) {
+            LOG_API("Couldn't initialize dispatchTab.ClosureSetArg");
+            return false;
+        }
+        dispatchTab.ClosureSetGlobal = (ClosureSetGlobalFnPtr)dlsym(handle, "rsClosureSetGlobal");
+        if (dispatchTab.ClosureSetGlobal == NULL) {
+            LOG_API("Couldn't initialize dispatchTab.ClosureSetGlobal");
+            return false;
+        }
+        dispatchTab.ScriptGroup2Create = (ScriptGroup2CreateFnPtr)dlsym(handle, "rsScriptGroup2Create");
+        if (dispatchTab.ScriptGroup2Create == NULL) {
+            LOG_API("Couldn't initialize dispatchTab.ScriptGroup2Create");
+            return false;
+        }
+    }
+
     return true;
+
 }
 
 
diff --git a/cpp/rsDispatch.h b/cpp/rsDispatch.h
index 066018d..c1c8d77 100644
--- a/cpp/rsDispatch.h
+++ b/cpp/rsDispatch.h
@@ -173,7 +173,7 @@
     AllocationGetPointerFnPtr AllocationGetPointer;
 };
 
-bool loadSymbols(void* handle, dispatchTable& dispatchTab);
+bool loadSymbols(void* handle, dispatchTable& dispatchTab, int device_api = 0);
 
 //USAGE_IO for RS Support lib
 typedef void (*sAllocationSetSurfaceFnPtr) (JNIEnv *, jobject, RsContext, RsAllocation, RsNativeWindow, dispatchTable);
diff --git a/driver/rsdAllocation.cpp b/driver/rsdAllocation.cpp
index 33649c3..f56ab56 100644
--- a/driver/rsdAllocation.cpp
+++ b/driver/rsdAllocation.cpp
@@ -472,58 +472,99 @@
     return true;
 }
 
+void rsdAllocationAdapterOffset(const Context *rsc, const Allocation *alloc) {
+    //ALOGE("rsdAllocationAdapterOffset");
+
+    // Get a base pointer to the new LOD
+    const Allocation *base = alloc->mHal.state.baseAlloc;
+    const Type *type = alloc->mHal.state.type;
+    if (base == nullptr) {
+        return;
+    }
+
+    uint8_t * ptrA = (uint8_t *)base->getPointerUnchecked(alloc->mHal.state.originX, alloc->mHal.state.originY);
+    uint8_t * ptrB = (uint8_t *)base->getPointerUnchecked(0, 0);
+
+    //ALOGE("rsdAllocationAdapterOffset  %p  %p", ptrA, ptrB);
+    //ALOGE("rsdAllocationAdapterOffset  lodCount %i", alloc->mHal.drvState.lodCount);
+
+    const int lodBias = alloc->mHal.state.originLOD;
+    uint32_t lodCount = rsMax(alloc->mHal.drvState.lodCount, (uint32_t)1);
+    for (uint32_t lod=0; lod < lodCount; lod++) {
+        alloc->mHal.drvState.lod[lod] = base->mHal.drvState.lod[lod + lodBias];
+        alloc->mHal.drvState.lod[lod].mallocPtr =
+                ((uint8_t *)alloc->mHal.drvState.lod[lod].mallocPtr + (ptrA - ptrB));
+        //ALOGE("rsdAllocationAdapterOffset  lod  %p  %i %i", alloc->mHal.drvState.lod[lod].mallocPtr, alloc->mHal.drvState.lod[lod].dimX, alloc->mHal.drvState.lod[lod].dimY);
+    }
+}
+
+bool rsdAllocationAdapterInit(const Context *rsc, Allocation *alloc) {
+    DrvAllocation *drv = (DrvAllocation *)calloc(1, sizeof(DrvAllocation));
+    if (!drv) {
+        return false;
+    }
+    alloc->mHal.drv = drv;
+
+    // We need to build an allocation that looks like a subset of the parent allocation
+    rsdAllocationAdapterOffset(rsc, alloc);
+
+    return true;
+}
+
 void rsdAllocationDestroy(const Context *rsc, Allocation *alloc) {
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
+    if (alloc->mHal.state.baseAlloc == nullptr) {
 #ifndef RS_COMPATIBILITY_LIB
-    if (drv->bufferID) {
-        // Causes a SW crash....
-        //ALOGV(" mBufferID %i", mBufferID);
-        //glDeleteBuffers(1, &mBufferID);
-        //mBufferID = 0;
-    }
-    if (drv->textureID) {
-        RSD_CALL_GL(glDeleteTextures, 1, &drv->textureID);
-        drv->textureID = 0;
-    }
-    if (drv->renderTargetID) {
-        RSD_CALL_GL(glDeleteRenderbuffers, 1, &drv->renderTargetID);
-        drv->renderTargetID = 0;
-    }
+        if (drv->bufferID) {
+            // Causes a SW crash....
+            //ALOGV(" mBufferID %i", mBufferID);
+            //glDeleteBuffers(1, &mBufferID);
+            //mBufferID = 0;
+        }
+        if (drv->textureID) {
+            RSD_CALL_GL(glDeleteTextures, 1, &drv->textureID);
+            drv->textureID = 0;
+        }
+        if (drv->renderTargetID) {
+            RSD_CALL_GL(glDeleteRenderbuffers, 1, &drv->renderTargetID);
+            drv->renderTargetID = 0;
+        }
 #endif
 
-    if (alloc->mHal.drvState.lod[0].mallocPtr) {
-        // don't free user-allocated ptrs or IO_OUTPUT buffers
-        if (!(drv->useUserProvidedPtr) &&
-            !(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_INPUT) &&
-            !(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT)) {
-                free(alloc->mHal.drvState.lod[0].mallocPtr);
+        if (alloc->mHal.drvState.lod[0].mallocPtr) {
+            // don't free user-allocated ptrs or IO_OUTPUT buffers
+            if (!(drv->useUserProvidedPtr) &&
+                !(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_INPUT) &&
+                !(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT)) {
+                    free(alloc->mHal.drvState.lod[0].mallocPtr);
+            }
+            alloc->mHal.drvState.lod[0].mallocPtr = nullptr;
         }
-        alloc->mHal.drvState.lod[0].mallocPtr = nullptr;
-    }
 
 #ifndef RS_COMPATIBILITY_LIB
-    if (drv->readBackFBO != nullptr) {
-        delete drv->readBackFBO;
-        drv->readBackFBO = nullptr;
-    }
-
-    if ((alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT) &&
-        (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT)) {
-
-        DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
-        ANativeWindow *nw = drv->wndSurface;
-        if (nw) {
-            GraphicBufferMapper &mapper = GraphicBufferMapper::get();
-            mapper.unlock(drv->wndBuffer->handle);
-            int32_t r = nw->queueBuffer(nw, drv->wndBuffer, -1);
-
-            drv->wndSurface = nullptr;
-            native_window_api_disconnect(nw, NATIVE_WINDOW_API_CPU);
-            nw->decStrong(nullptr);
+        if (drv->readBackFBO != nullptr) {
+            delete drv->readBackFBO;
+            drv->readBackFBO = nullptr;
         }
-    }
+
+        if ((alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT) &&
+            (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT)) {
+
+            DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+            ANativeWindow *nw = drv->wndSurface;
+            if (nw) {
+                GraphicBufferMapper &mapper = GraphicBufferMapper::get();
+                mapper.unlock(drv->wndBuffer->handle);
+                int32_t r = nw->queueBuffer(nw, drv->wndBuffer, -1);
+
+                drv->wndSurface = nullptr;
+                native_window_api_disconnect(nw, NATIVE_WINDOW_API_CPU);
+                nw->decStrong(nullptr);
+            }
+        }
 #endif
+    }
 
     free(drv);
     alloc->mHal.drv = nullptr;
diff --git a/driver/rsdAllocation.h b/driver/rsdAllocation.h
index 9f13c98..0b6d8bf 100644
--- a/driver/rsdAllocation.h
+++ b/driver/rsdAllocation.h
@@ -84,6 +84,8 @@
 bool rsdAllocationInit(const android::renderscript::Context *rsc,
                        android::renderscript::Allocation *alloc,
                        bool forceZero);
+bool rsdAllocationAdapterInit(const android::renderscript::Context *rsc,
+                              android::renderscript::Allocation *alloc);
 void rsdAllocationDestroy(const android::renderscript::Context *rsc,
                           android::renderscript::Allocation *alloc);
 
@@ -175,5 +177,8 @@
                                      const android::renderscript::Allocation *alloc,
                                      android::renderscript::rs_allocation *obj);
 
+void rsdAllocationAdapterOffset(const android::renderscript::Context *rsc,
+                                const android::renderscript::Allocation *alloc);
+
 
 #endif
diff --git a/driver/rsdCore.cpp b/driver/rsdCore.cpp
index 0181282..8812446 100644
--- a/driver/rsdCore.cpp
+++ b/driver/rsdCore.cpp
@@ -88,6 +88,7 @@
 
     {
         rsdAllocationInit,
+        rsdAllocationAdapterInit,
         rsdAllocationDestroy,
         rsdAllocationGrallocBits,
         rsdAllocationResize,
@@ -110,7 +111,8 @@
         rsdAllocationElementData1D,
         rsdAllocationElementData2D,
         rsdAllocationGenerateMipmaps,
-        rsdAllocationUpdateCachedObject
+        rsdAllocationUpdateCachedObject,
+        rsdAllocationAdapterOffset
     },
 
 
diff --git a/driver/runtime/arch/generic.c b/driver/runtime/arch/generic.c
index 710fbee..c7a717f 100644
--- a/driver/runtime/arch/generic.c
+++ b/driver/runtime/arch/generic.c
@@ -642,21 +642,6 @@
     return convert_uchar4(p);
 }
 
-static float4 yuv_U_values = {0.f, -0.392f * 0.003921569f, +2.02 * 0.003921569f, 0.f};
-static float4 yuv_V_values = {1.603f * 0.003921569f, -0.815f * 0.003921569f, 0.f, 0.f};
-
-extern float4 __attribute__((overloadable)) rsYuvToRGBA_float4(uchar y, uchar u, uchar v) {
-    float4 color = (float)y * 0.003921569f;
-    float4 fU = ((float)u) - 128.f;
-    float4 fV = ((float)v) - 128.f;
-
-    color += fU * yuv_U_values;
-    color += fV * yuv_V_values;
-    color = clamp(color, 0.f, 1.f);
-    return color;
-}
-
-
 /*
  * half_RECIP
  */
diff --git a/driver/runtime/rs_convert.c b/driver/runtime/rs_convert.c
index c998edc..53c321b 100644
--- a/driver/runtime/rs_convert.c
+++ b/driver/runtime/rs_convert.c
@@ -52,3 +52,22 @@
 CVT_FUNC(float)
 CVT_FUNC(double)
 
+
+/*
+ * YUV float4 version
+ */
+
+static float4 yuv_U_values = {0.f, -0.392f * 0.003921569f, +2.02 * 0.003921569f, 0.f};
+static float4 yuv_V_values = {1.603f * 0.003921569f, -0.815f * 0.003921569f, 0.f, 0.f};
+
+extern float4 __attribute__((overloadable)) rsYuvToRGBA_float4(uchar y, uchar u, uchar v) {
+    float4 color = (float)y * 0.003921569f;
+    float4 fU = ((float)u) - 128.f;
+    float4 fV = ((float)v) - 128.f;
+
+    color += fU * yuv_U_values;
+    color += fV * yuv_V_values;
+    color = clamp(color, 0.f, 1.f);
+    return color;
+}
+
diff --git a/driver/runtime/rs_structs.h b/driver/runtime/rs_structs.h
index b57a2c8..76c2156 100644
--- a/driver/runtime/rs_structs.h
+++ b/driver/runtime/rs_structs.h
@@ -45,6 +45,15 @@
             int32_t surfaceTextureID;
             void * nativeBuffer;
             int64_t timestamp;
+
+            // Allocation adapter state
+            const void *baseAlloc;
+            uint32_t originX;
+            uint32_t originY;
+            uint32_t originZ;
+            uint32_t originLOD;
+            uint32_t originFace;
+            uint32_t originArray[4/*Type::mMaxArrays*/];
         } state;
 
         struct DrvState {
@@ -63,6 +72,9 @@
                 uint32_t shift;
                 uint32_t step;
             } yuv;
+
+            int grallocFlags;
+            uint32_t dimArray[4/*Type::mMaxArrays*/];
         } drvState;
     } mHal;
 } Allocation_t;
diff --git a/java/tests/VrDemo/Android.mk b/java/tests/VrDemo/Android.mk
new file mode 100644
index 0000000..061af75
--- /dev/null
+++ b/java/tests/VrDemo/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SDK_VERSION := 21
+
+LOCAL_PACKAGE_NAME := VrDemo
+
+include $(BUILD_PACKAGE)
diff --git a/java/tests/VrDemo/AndroidManifest.xml b/java/tests/VrDemo/AndroidManifest.xml
new file mode 100644
index 0000000..9f5930c
--- /dev/null
+++ b/java/tests/VrDemo/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="rsexample.google.com.vrdemo" >
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application
+        android:largeHeap="true"
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name="com.example.android.rs.vr.VrActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/java/tests/VrDemo/_index.html b/java/tests/VrDemo/_index.html
new file mode 100644
index 0000000..51f5480
--- /dev/null
+++ b/java/tests/VrDemo/_index.html
@@ -0,0 +1,80 @@
+<h1>A Volume Renderer</h1>
+<h2>To setup to load DICOM or RAW data</h2>
+<p> 
+<ol>
+  <li>Create directory <b>/sdcard/Download/volumes/</b></li>
+  <li>Create subdirectories for each dataset</li>
+  <li>Create a ".prop" for each data set</li>
+</ol>
+
+".prop" files are simple jave properties files which consist 
+of key=value pairs 
+
+<dl>
+  <dt>name</dt>
+  <dd>The name to be desplayed on the screen</dd>
+  <dt>dir</dt>
+  <dd>The subdirectory containing the data</dd>
+  <dt>format</dt>
+  <dd>the format the data is in (raw or dicom)</dd>
+  <dt>format</dt>
+  <dd>The format of the pixes (only short & ushort supported)</dd>
+  <dt>dim</dt>
+  <dd>the X by Y by Z dimensions of the data seperated by x</dd>
+  <dt>voxeldim</dt>
+  <dd>The size of each voxel separated by a comma "," uniform = (1.0,1.0,1.0)</dd>
+  <dt>sequence</dt>
+  <dd>if it says 1file the raw data is all on one file</dd>
+  <dt>looks</dt>
+  <dd>The name of the supplied looks</dd>
+  <dt>XXXXX.opacity</dt>
+  <dd>the opacity table asociate with the XXXXX look</dd>
+  <dt>XXXXX.color</dt>
+  <dd>The color table asociated with the XXXXX look</dd>
+</dl>
+</p>
+<h3>Opacity table</h3>
+opacity tabls consisit of nested arrays of shorts.
+The first being intensity the second being opacity on a 0-255 scale.
+The values for each pixel in the volume are interpolated 
+<p></p>
+<h3>Color table</h3>
+The color table 
+The values are linearly interpolated from one value to the next 
+
+<dl>
+  <dt>intensity</dt>
+  <dd>The intensity that has this "color"</dd>
+  <dt>RGB</dt>
+  <dd>Hex number describing the color this number has</dd>
+  <dt>diffuse</dt>
+  <dd>how much difuse lighting to apply to this materal 0-100</dd>
+  <dt>ambient</dt>
+  <dd>how much ambient lighting does this materal reflect 0-100</dd>
+  <dt>specular</dt>
+  <dd>How much specular reflection materal has 0-100</dd>
+</dl>
+<p></p>
+<h3>Example property file</h3>
+<p>
+<PRE>
+name=Back Pack
+dir=backpack
+format=raw
+endian=little
+pixels=ushort
+dim=512x512x373
+voxeldim=0.9766, 0.9766, 1.25
+sequence=1file
+looks=clean,clay,black
+black.opacity = { {255, 0},{259, 255}};
+black.color = {{255, 0xff0000, 10, 10, 20},{259, 0xfff5e4, 10, 10, 20}};
+clay.opacity = { {27, 0}, {92, 0}, {629, 0}, {1203, 255}, {2343, 255}}
+clay.color = {{27, 0xe1ac96, 30, 70, 20}, {92, 0xbc703a, 30, 70, 20}, {629, 0xe29c9d, 30, 70, 20}, {1203, 0xe1af97, 30, 34, 20}, {2343, 0xdebd9d, 30, 38, 20}}
+clean.opacity = {{27, 0}, {92, 0}, {1433, 0}, {1691, 255}, {1734, 255}}
+clean.color = {{27, 0xe1ac96, 30, 70, 20}, {92, 0xe1ac96, 30, 70, 20}, {629, 0xed1904, 30, 70, 0}, {1611, 0xaeb3c9, 19, 42, 44}, {1826, 0xe7e7ea, 19, 44, 41}}
+</PRE>
+
+
+ </p>
+
diff --git a/java/tests/VrDemo/res/drawable-hdpi/ic_launcher.png b/java/tests/VrDemo/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/java/tests/VrDemo/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/VrDemo/res/drawable-mdpi/ic_launcher.png b/java/tests/VrDemo/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/java/tests/VrDemo/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/VrDemo/res/drawable-xhdpi/ic_launcher.png b/java/tests/VrDemo/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/java/tests/VrDemo/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/VrDemo/res/drawable-xxhdpi/ic_launcher.png b/java/tests/VrDemo/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/java/tests/VrDemo/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/VrDemo/res/layout-land/activity_vr.xml b/java/tests/VrDemo/res/layout-land/activity_vr.xml
new file mode 100644
index 0000000..3ab5662
--- /dev/null
+++ b/java/tests/VrDemo/res/layout-land/activity_vr.xml
@@ -0,0 +1,111 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:orientation="horizontal"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".VrActivity">
+    <view
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        class="com.example.android.rs.vr.VrView"
+        android:id="@+id/view"
+        android:layout_weight="1"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:id="@+id/title"
+                    android:text="@string/volume_name"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1.0"
+                    android:gravity="center" />
+            </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+
+                <ToggleButton
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textOn="@string/xcut"
+                    android:textOff="@string/xcut"
+                    android:onClick="cutXClick" />
+                <ToggleButton
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textOff="@string/ycut"
+                    android:textOn="@string/ycut"
+                    android:onClick="cutYClick" />
+                <ToggleButton
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textOn="@string/zcut"
+                    android:textOff="@string/zcut"
+                    android:onClick="cutZClick" />
+                <ToggleButton
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textOn="@string/rotate"
+                    android:textOff="@string/rotate"
+                    android:onClick="rotateClick"/>
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/looks"
+                    android:onClick="looksClick"/>
+
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/load"
+                    android:onClick="dataClick"/>
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/reset"
+                    android:onClick="resetClick" />
+
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/save"
+                    android:onClick="saveClick" />
+            </LinearLayout>
+
+        </LinearLayout>
+ </LinearLayout>
diff --git a/java/tests/VrDemo/res/layout/activity_vr.xml b/java/tests/VrDemo/res/layout/activity_vr.xml
new file mode 100644
index 0000000..d121f4f
--- /dev/null
+++ b/java/tests/VrDemo/res/layout/activity_vr.xml
@@ -0,0 +1,111 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".VrActivity">
+    <view
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        class="com.example.android.rs.vr.VrView"
+        android:id="@+id/view"
+        android:layout_weight="1"/>
+
+     <LinearLayout
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:orientation="vertical">
+         <LinearLayout
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:orientation="horizontal">
+
+             <TextView
+                 android:id="@+id/title"
+                 android:text="@string/volume_name"
+                 android:layout_width="0dp"
+                 android:layout_height="match_parent"
+                 android:layout_weight="1.0"
+                 android:gravity="center" />
+         </LinearLayout>
+
+         <LinearLayout
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:orientation="horizontal">
+
+             <ToggleButton
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:textOn="@string/xcut"
+                 android:textOff="@string/xcut"
+                 android:onClick="cutXClick" />
+             <ToggleButton
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:textOff="@string/ycut"
+                 android:textOn="@string/ycut"
+                 android:onClick="cutYClick" />
+             <ToggleButton
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:textOn="@string/zcut"
+                 android:textOff="@string/zcut"
+                 android:onClick="cutZClick" />
+             <ToggleButton
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:textOn="@string/rotate"
+                 android:textOff="@string/rotate"
+                 android:onClick="rotateClick"/>
+         </LinearLayout>
+
+         <LinearLayout
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:orientation="horizontal">
+
+             <Button
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:text="@string/looks"
+                 android:onClick="looksClick"/>
+
+             <Button
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:text="@string/load"
+                 android:onClick="dataClick"/>
+             <Button
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:text="@string/reset"
+                 android:onClick="resetClick" />
+
+             <Button
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:text="@string/save"
+                 android:onClick="saveClick" />
+         </LinearLayout>
+
+     </LinearLayout>
+</LinearLayout>
diff --git a/java/tests/VrDemo/res/values-v21/styles.xml b/java/tests/VrDemo/res/values-v21/styles.xml
new file mode 100644
index 0000000..6e7e101
--- /dev/null
+++ b/java/tests/VrDemo/res/values-v21/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <style name="AppTheme" parent="android:Theme.Material.Light">
+    </style>
+</resources>
diff --git a/java/tests/VrDemo/res/values-w820dp/dimens.xml b/java/tests/VrDemo/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/java/tests/VrDemo/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/java/tests/VrDemo/res/values/dimens.xml b/java/tests/VrDemo/res/values/dimens.xml
new file mode 100644
index 0000000..248dfff
--- /dev/null
+++ b/java/tests/VrDemo/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/java/tests/VrDemo/res/values/strings.xml b/java/tests/VrDemo/res/values/strings.xml
new file mode 100644
index 0000000..accf51e
--- /dev/null
+++ b/java/tests/VrDemo/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+
+    <string name="app_name">Volume Rendering</string>
+    <string name="rotate">Rotate</string>
+    <string name="save">save</string>
+    <string name="reset">Reset</string>
+    <string name="load">Load</string>
+    <string name="looks">Looks</string>
+    <string name="zcut">Z Cut</string>
+    <string name="ycut">Y Cut</string>
+    <string name="xcut">X Cut</string>
+    <string name="volume_name">Volume Name</string>
+
+</resources>
diff --git a/java/tests/VrDemo/res/values/styles.xml b/java/tests/VrDemo/res/values/styles.xml
new file mode 100644
index 0000000..0f1c956
--- /dev/null
+++ b/java/tests/VrDemo/res/values/styles.xml
@@ -0,0 +1,22 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+    </style>
+
+</resources>
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/VrActivity.java b/java/tests/VrDemo/src/com/example/android/rs/vr/VrActivity.java
new file mode 100644
index 0000000..aa64d2a
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/VrActivity.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.renderscript.RenderScript;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.PopupMenu;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import java.io.File;
+
+import rsexample.google.com.vrdemo.R;
+
+import com.example.android.rs.vr.engine.Volume;
+import com.example.android.rs.vr.engine.VrState;
+import com.example.android.rs.vr.loaders.VolumeLoader;
+
+/**
+ * basic activity loads the volume and sets it on the VrView
+ */
+public class VrActivity extends Activity {
+    private static final String LOGTAG = "VrActivity";
+    VrState mState = new VrState();
+    VrView mVrView;
+    VolumeLoader mLoader;
+    private RenderScript mRs;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_vr);
+
+        mVrView = (VrView) findViewById(R.id.view);
+        mRs = RenderScript.create(VrActivity.this);
+
+        String dir = "/sdcard/Download/volumes";
+        mLoader = new VolumeLoader(dir);
+        VrSetupTask setup = new VrSetupTask();
+        String [] names = mLoader.getNames();
+        setup.execute(names[0]);
+        TextView tv = (TextView) findViewById(R.id.title);
+        tv.setText(names[0]);
+    }
+
+    class VrSetupTask extends AsyncTask<String, Integer, Volume> {
+        ProgressDialog progressDialog;
+
+        protected void onPreExecute() {
+            super.onPreExecute();
+            progressDialog = new ProgressDialog(VrActivity.this);
+            progressDialog.setMessage( "Loading Volume");
+            progressDialog.setIndeterminate(true);
+            progressDialog.setCancelable(false);
+            progressDialog.setProgress(0);
+            progressDialog.setMax(100);
+            progressDialog.show();
+
+            mLoader.setProgressListener(new VolumeLoader.ProgressListener() {
+                @Override
+                public void progress(int n, int total) {
+                     publishProgress(n, total);
+                }
+            });
+        }
+
+        @Override
+        protected Volume doInBackground(String... names) {
+            return  mLoader.getVolume(mRs, names[0]);
+        }
+
+        @Override
+        protected void onProgressUpdate(Integer... progress) {
+            progressDialog.setMessage("load "+progress[0]+"/"+progress[1]);
+            progressDialog.setMax(progress[1]);
+            progressDialog.setProgress(progress[0]);
+            Log.v(LOGTAG,"Loading "+ progress[0]+"/"+progress[1]);
+        }
+
+        protected void onPostExecute(Volume v) {
+            Log.v(LOGTAG,"done");
+            mVrView.setVolume(mRs, v);
+            progressDialog.dismiss();
+        }
+    }
+
+    public void cutXClick(View v) {
+        mVrView.setMode(VrView.CUT_X_MODE);
+        uncheckOthers(v);
+    }
+
+    public void cutYClick(View v) {
+        mVrView.setMode(VrView.CUT_Y_MODE);
+        uncheckOthers(v);
+    }
+
+    public void cutZClick(View v) {
+        mVrView.setMode(VrView.CUT_Z_MODE);
+        uncheckOthers(v);
+    }
+
+    public void rotateClick(View v) {
+        mVrView.setMode(VrView.ROTATE_MODE);
+        uncheckOthers(v);
+    }
+
+    public void resetClick(View v) {
+        mVrView.resetCut();
+    }
+
+    public void saveClick(View v) {
+        // TODO should save and Image
+    }
+
+    public void looksClick(View v) {
+        String[] looks = mVrView.getLooks();
+        PopupMenu popup = new PopupMenu(this, v);
+        Menu menu = popup.getMenu();
+
+        for (int i = 0; i < looks.length; i++) {
+            menu.add(0, Menu.FIRST + i, Menu.NONE, looks[i]);
+        }
+
+        //registering popup with OnMenuItemClickListener
+        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+                mVrView.setLook(item.getTitle().toString());
+                return true;
+            }
+        });
+        popup.show();
+        uncheckOthers(v);
+    }
+
+    public void dataClick(View v) {
+        Log.v(LOGTAG, "dataClick");
+
+        String[] volumes = mLoader.getNames();
+        PopupMenu popup = new PopupMenu(this, v);
+        Menu menu = popup.getMenu();
+
+        for (int i = 0; i < volumes.length; i++) {
+            menu.add(0, Menu.FIRST + i, Menu.NONE, volumes[i]);
+        }
+
+        //registering popup with OnMenuItemClickListener
+        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+
+                VrSetupTask setup = new VrSetupTask();
+                String title = item.getTitle().toString();
+                TextView tv = (TextView) findViewById(R.id.title);
+                tv.setText(title);
+                setup.execute(title);
+                return true;
+            }
+        });
+
+        popup.show();
+        uncheckOthers(v);
+    }
+
+    private void uncheckOthers(View v) {
+        ViewGroup p = (ViewGroup) v.getParent().getParent();
+        uncheckOthers(p, v);
+    }
+
+    private void uncheckOthers(ViewGroup p, View v) {
+        int n = p.getChildCount();
+        for (int i = 0; i < n; i++) {
+            final View child = p.getChildAt(i);
+            if (child instanceof ViewGroup) {
+                uncheckOthers((ViewGroup) child, v);
+            }
+            if (v != child && child instanceof ToggleButton) {
+                ToggleButton t = (ToggleButton) child;
+                t.setChecked(false);
+
+            }
+        }
+    }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java b/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java
new file mode 100644
index 0000000..b4ce67a
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.SurfaceTexture;
+import android.os.AsyncTask;
+import android.renderscript.RenderScript;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.Surface;
+import android.view.TextureView;
+
+import com.example.android.rs.vr.engine.Cube;
+import com.example.android.rs.vr.engine.Pipeline;
+import com.example.android.rs.vr.engine.RsBrickedBitMask;
+import com.example.android.rs.vr.engine.TriData;
+import com.example.android.rs.vr.engine.VectorUtil;
+import com.example.android.rs.vr.engine.ViewMatrix;
+import com.example.android.rs.vr.engine.Volume;
+import com.example.android.rs.vr.engine.VrPipline1;
+import com.example.android.rs.vr.engine.VrState;
+
+import java.util.Arrays;
+
+/**
+ * VrView runs a volume rendering on the screen
+ */
+public class VrView extends TextureView {
+    private static final String LOGTAG = "rsexample.google.com.vrdemo";
+    private Pipeline mPipline = new VrPipline1();//BasicPipline();
+    //    private VrState mState4 = new VrState(); // for down sampled
+    private VrState mState1 = new VrState(); // for full res version
+    private VrState mStateLow = new VrState(); // for full res version
+    private VrState mLastDrawn = new VrState(); // for full res version
+    private Paint paint = new Paint();
+    private SurfaceTexture mSurfaceTexture;
+    private Surface mSurface;
+    ///private Size mImageViewSize;
+    private int refresh = 0;  // 0 is no refresh else refresh = downsample
+    int mPreviousMode = -1;
+    int last_look = 0;
+
+    //    int mDownSample = 4;
+    private final char[] looks = {
+            ViewMatrix.UP_AT,
+            ViewMatrix.DOWN_AT,
+            ViewMatrix.RIGHT_AT,
+            ViewMatrix.LEFT_AT,
+            ViewMatrix.FORWARD_AT,
+            ViewMatrix.BEHIND_AT};
+    private byte mMode = ROTATE_MODE;
+    private ScaleGestureDetector mScaleDetector;
+    private boolean mInScale;
+
+    public static final byte ROTATE_MODE = 1;
+    public static final byte CUT_X_MODE = 2;
+    public static final byte CUT_Y_MODE = 3;
+    public static final byte CUT_Z_MODE = 4;
+
+    public void setMode(byte mode) {
+        mMode = mode;
+    }
+
+    private float mDownPointX;
+    private float mDownPointY;
+    private double mDownScreenWidth;
+    private double[] mDownLookPoint = new double[3];
+    private double[] mDownEyePoint = new double[3];
+    private double[] mDownUpVector = new double[3];
+    private double[] mDownRightVector = new double[3];
+    private float[] mCurrentTrim = new float[6];
+    VrRenderTesk mRenderTesk;
+    VrBinGridTask mBinGridTask;
+
+    public VrView(Context context) {
+        super(context);
+        setup(context);
+        paint.setFilterBitmap(true);
+    }
+
+    public VrView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setup(context);
+    }
+
+
+    public VrView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setup(context);
+    }
+
+
+    public VrView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setup(context);
+    }
+
+    private void setup(Context context) {
+        setBackgroundColor(Color.BLACK);
+        if (isInEditMode()) {
+            return;
+        }
+        setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
+
+            @Override
+            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+                mSurfaceTexture = surface;
+            }
+
+            @Override
+            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+                mSurfaceTexture = surface;
+            }
+
+            @Override
+            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+                return false;
+            }
+
+            @Override
+            public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+            }
+        });
+
+        mScaleDetector = new ScaleGestureDetector(context,
+                new ScaleGestureDetector.OnScaleGestureListener() {
+                    @Override
+                    public boolean onScale(ScaleGestureDetector detector) {
+                        double width = mState1.mTransform.getScreenWidth() / detector.getScaleFactor();
+                        mState1.mTransform.setScreenWidth(width);
+                        panMove(detector.getFocusX(), detector.getFocusY());
+                        render(4);
+                        return true;
+                    }
+
+                    @Override
+                    public boolean onScaleBegin(ScaleGestureDetector detector) {
+                        panDown(detector.getFocusX(), detector.getFocusY());
+                        mInScale = true;
+                        return true;
+                    }
+
+                    @Override
+                    public void onScaleEnd(ScaleGestureDetector detector) {
+                        mInScale = false;
+                    }
+                });
+    }
+
+    private void updateOutputDimensions(int width, int height) {
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent e) {
+        if (mPreviousMode == 1) {
+            mPipline.cancel();
+        }
+        boolean handled = mScaleDetector.onTouchEvent(e);
+        if (e.getPointerCount() > 1) {
+            return true;
+        }
+        if (mInScale) {
+            return true;
+        }
+        int action = e.getAction();
+        if (action == MotionEvent.ACTION_DOWN) {
+            actionDown(e);
+        } else if (action == MotionEvent.ACTION_MOVE) {
+            actionMove(e);
+            render(4);
+        } else if (action == MotionEvent.ACTION_UP) {
+            actionUp(e);
+            refresh = 1;
+            render(1);
+        }
+        return true;
+    }
+
+    private void panMove(float x, float y) {
+        double dist_x = (mDownPointX - x) * mDownScreenWidth / getWidth();
+        double dist_y = (y - mDownPointY) * mDownScreenWidth / getWidth();
+        double[] p;
+        p = mState1.mTransform.getEyePoint();
+        p[0] = mDownEyePoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0];
+        p[1] = mDownEyePoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1];
+        p[2] = mDownEyePoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2];
+        mState1.mTransform.setEyePoint(p);
+        p = mState1.mTransform.getLookPoint();
+        p[0] = mDownLookPoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0];
+        p[1] = mDownLookPoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1];
+        p[2] = mDownLookPoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2];
+        mState1.mTransform.setLookPoint(p);
+    }
+
+    private void panDown(float x, float y) {
+        mDownPointX = x;
+        mDownPointY = y;
+        mDownScreenWidth = mState1.mTransform.getScreenWidth();
+        double[] p;
+        p = mState1.mTransform.getLookPoint();
+        System.arraycopy(p, 0, mDownLookPoint, 0, 3);
+        p = mState1.mTransform.getEyePoint();
+        System.arraycopy(p, 0, mDownEyePoint, 0, 3);
+        p = mState1.mTransform.getUpVector();
+        System.arraycopy(p, 0, mDownUpVector, 0, 3);
+        mDownRightVector[0] = mDownLookPoint[0] - mDownEyePoint[0];
+        mDownRightVector[1] = mDownLookPoint[1] - mDownEyePoint[1];
+        mDownRightVector[2] = mDownLookPoint[2] - mDownEyePoint[2];
+        VectorUtil.normalize(mDownRightVector);
+        VectorUtil.cross(mDownRightVector, mDownUpVector, mDownRightVector);
+    }
+
+    private void actionDown(MotionEvent e) {
+        panDown(e.getX(), e.getY());
+
+        switch (mMode) {
+            case ROTATE_MODE:
+                mState1.mTransform.trackBallDown(e.getX(), e.getY());
+                break;
+
+            case CUT_X_MODE:
+            case CUT_Y_MODE:
+            case CUT_Z_MODE:
+                float[] trim = mState1.mCubeVolume.getTrim();
+                System.arraycopy(trim, 0, mCurrentTrim, 0, 6);
+                break;
+        }
+    }
+
+    private void actionMove(MotionEvent e) {
+        float deltax, deltay;
+
+        switch (mMode) {
+            case ROTATE_MODE:
+
+                mState1.mTransform.trackBallMove(e.getX(), e.getY());
+
+                break;
+
+            case CUT_X_MODE:
+                deltax = (float) ((mDownPointX - e.getX()) / getWidth());
+                deltay = (float) -((mDownPointY - e.getY()) / getWidth());
+                cut(0, deltax, deltay);
+                break;
+            case CUT_Y_MODE:
+                deltax = (float) ((mDownPointX - e.getX()) / getWidth());
+                deltay = (float) -((mDownPointY - e.getY()) / getWidth());
+                cut(1, deltax, deltay);
+                break;
+            case CUT_Z_MODE:
+                deltax = (float) ((mDownPointX - e.getX()) / getWidth());
+                deltay = (float) -((mDownPointY - e.getY()) / getWidth());
+                cut(2, deltax, deltay);
+                break;
+
+        }
+    }
+
+    private void actionUp(MotionEvent e) {
+    }
+
+    public void cut(int side, float fractionx, float fractiony) {
+        float[] f = Arrays.copyOf(mCurrentTrim, mCurrentTrim.length);
+        f[side] += fractionx;
+        if (f[side] < 0) f[side] = 0;
+        if (f[side] > .8) f[side] = .8f;
+        f[side + 3] += fractiony;
+        if (f[side + 3] < 0) f[side + 3] = 0;
+        if (f[side + 3] > .8) f[side + 3] = .8f;
+        mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, f);
+        mState1.mCubeScreen = new TriData(mState1.mCubeVolume);
+        mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim);
+    }
+
+    public void resetCut() {
+        Arrays.fill(mCurrentTrim, 0);
+        mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, mCurrentTrim);
+        mState1.mCubeScreen = new TriData(mState1.mCubeVolume);
+        mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim);
+        mState1.mTransform.look(looks[last_look], mState1.mCubeScreen, getWidth(), getHeight());
+        mState1.mTransform.setScreenWidth(.6f * mState1.mTransform.getScreenWidth());
+        last_look = (last_look + 1) % looks.length;
+        render(4);
+    }
+
+    public void setVolume(RenderScript rs, Volume v) {
+        mState1.mRs = rs;
+        mState1.mVolume = v;
+        mState1.mCubeVolume = new Cube(mState1.mVolume, 5f);
+        mState1.mCubeScreen = new TriData(mState1.mCubeVolume);
+        mState1.mCubeScreen.scale(v.mVoxelDim);
+        mState1.mTransform.setVoxelDim(v.mVoxelDim);
+        mState1.mTransform.look(ViewMatrix.DOWN_AT, mState1.mCubeScreen, getWidth(), getHeight());
+        setLook(mState1.mVolume.getLookNames()[0]);
+    }
+
+    protected void look(int k) {
+        mState1.mTransform.look(looks[k], mState1.mCubeVolume, getWidth(), getHeight());
+        render(4);
+        render(1);
+    }
+
+    void render(int downSample) {
+
+        if (mRenderTesk == null) {
+            mRenderTesk = new VrRenderTesk();
+            refresh = 0;
+            mRenderTesk.execute(downSample);
+        } else {
+            refresh = downSample;
+        }
+    }
+
+    public String[] getLooks() {
+        return mState1.mVolume.getLookNames();
+    }
+
+    public void setLook(String look) {
+        int[][] color = mState1.mVolume.getLookColor(look);
+        int[][] opacity = mState1.mVolume.getLookOpactiy(look);
+        mState1.mMaterial.setup(opacity, color);
+        if (mBinGridTask == null) {
+            mBinGridTask = new VrBinGridTask();
+            mBinGridTask.execute(mState1.mVolume);
+        }
+    }
+
+    class VrRenderTesk extends AsyncTask<Integer, String, Long> {
+
+        long m_last_time;
+
+        @Override
+        protected void onPreExecute() {
+            mStateLow.copyData(mState1);
+        }
+
+        @Override
+        protected void onCancelled() {
+            mPipline.cancel();
+        }
+
+        @Override
+        protected Long doInBackground(Integer... down) {
+            if (mState1.mRs == null) return 0L;
+            if (mSurfaceTexture == null) return 0L;
+            int sample = 4;
+            VrState state = mStateLow;
+            if (down[0] == 1) {
+                if (mPreviousMode == 4) {
+                    mState1.copyData(mLastDrawn);
+                } else {
+                    mState1.copyData(mStateLow);
+                }
+                // mStateLow.mScrAllocation.setSurface(null);
+                state = mState1;
+                sample = 1;
+                if (mStateLow.mScrAllocation != null) {
+                    mStateLow.mScrAllocation.setSurface(null);
+                }
+            } else {
+                if (mState1.mScrAllocation != null) {
+                    mState1.mScrAllocation.setSurface(null);
+                }
+            }
+
+            if (mPreviousMode != sample) {
+                if (mSurface != null) {
+                    mSurface.release();
+                }
+                mSurface = new Surface(mSurfaceTexture);
+            }
+            mPreviousMode = sample;
+
+            int img_width = getWidth() / sample;
+            int img_height = getHeight() / sample;
+            state.createOutputAllocation(mSurface, img_width, img_height);
+
+            mPipline.initBuffers(state);
+
+            if (mPipline.isCancel()) {
+                return 0L;
+            }
+            long start = System.nanoTime();
+            addTimeLine(null);
+            mPipline.setupTriangles(state);
+
+            if (mPipline.isCancel()) {
+                return 0L;
+            }
+            mPipline.rasterizeTriangles(state);
+
+            if (mPipline.isCancel()) {
+                return 0L;
+            }
+            mPipline.raycast(state);
+
+            if (mPipline.isCancel()) {
+                return 0L;
+            }
+            mLastDrawn.copyData(state);
+            state.mRs.finish();
+            state.mScrAllocation.ioSend();
+
+            long time = System.nanoTime();
+            addLine("vr(" + img_width + "," + img_height + "): " + (time - start) / 1E6f + " ms");
+            return 0L;
+        }
+
+        private void addTimeLine(String line) {
+            if (line == null) {
+                m_last_time = System.nanoTime();
+                return;
+            }
+            long time = System.nanoTime();
+            float ftime = (time - m_last_time) / 1E6f;
+            if (ftime > 100)
+                addLine(line + ": " + (ftime / 1E3f) + " sec");
+            else
+                addLine(line + ": " + (ftime) + " ms");
+            m_last_time = System.nanoTime();
+        }
+
+        private void addLine(String line) {
+            publishProgress(line);
+        }
+
+        protected void onProgressUpdate(String... progress) {
+            Log.v(LOGTAG, progress[0]);
+        }
+
+        protected void onPostExecute(Long result) {
+            invalidate();
+            mRenderTesk = null;
+            if (refresh != 0) {
+                render(refresh);
+            }
+        }
+    }
+
+    class VrBinGridTask extends AsyncTask<Volume, String, Long> {
+
+        @Override
+        protected Long doInBackground(Volume... v) {
+            mState1.mRsMask = new RsBrickedBitMask(mState1);
+            mState1.mRs.finish();
+            return 0L;
+        }
+
+        protected void onProgressUpdate(String... progress) {
+            Log.v(LOGTAG, progress[0]);
+        }
+
+        protected void onPostExecute(Long result) {
+            mBinGridTask = null;
+            render(4);
+            render(1);
+        }
+    }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/BasicPipeline.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/BasicPipeline.java
new file mode 100644
index 0000000..6cec0b0
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/BasicPipeline.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.util.Log;
+
+/**
+ * Base implementation of a rendering pipeline Simply renders a box
+ */
+public class BasicPipeline implements Pipeline {
+    boolean mCancel = false;
+    private static final String LOGTAG = "BasicPipeline";
+    ScriptC_rasterize scriptC_rasterize;
+
+    @Override
+    public void cancel() {
+        mCancel = true;
+    }
+
+    @Override
+    public boolean isCancel() {
+        return mCancel;
+    }
+
+    @Override
+    public void initBuffers(VrState state) {
+        mCancel = false;
+    }
+
+    @Override
+    public void setupTriangles(VrState state) {
+        Matrix m = state.mTransform.getMatrix(Transform.VOLUME_SPACE, Transform.SCREEN_SPACE);
+        state.mCubeVolume.transform(m, state.mCubeScreen);
+    }
+
+    @Override
+    public void rasterizeTriangles(VrState state) {
+        if (scriptC_rasterize == null) {
+            scriptC_rasterize = new ScriptC_rasterize(state.mRs);
+        }
+        scriptC_rasterize.set_index(state.mCubeScreen.mIndex);
+        scriptC_rasterize.set_vert(state.mCubeScreen.mVert);
+        long start = System.nanoTime();
+        scriptC_rasterize.invoke_setup_triangles(state.mImgWidth, state.mImgHeight);
+        if (mCancel) return;
+        scriptC_rasterize.forEach_render_z(state.mzRangeFullAllocation);
+        state.mRs.finish();
+        Log.v(LOGTAG,"render triangles "+((System.nanoTime()-start)/1E6f)+" ms");
+    }
+
+    @Override
+    public void raycast(VrState state) {
+        scriptC_rasterize.set_z_range_buff(state.mzRangeFullAllocation);
+        scriptC_rasterize.forEach_draw_z_buffer(state.mzRangeFullAllocation, state.mScrAllocation);
+    }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Cube.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Cube.java
new file mode 100644
index 0000000..040b86f
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Cube.java
@@ -0,0 +1,110 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package com.example.android.rs.vr.engine;

+

+import java.text.*;

+import java.util.Arrays;

+

+public class Cube extends TriData {

+

+    private float[] mTrim = {0, 0, 0, 0, 0, 0 };

+

+    public Cube(Volume v, float delta) {

+        this(v, delta, new float[]{0, 0, 0, 0, 0, 0});

+    }

+

+    public Cube(Volume v, float delta, float[] trim) {

+        this();

+        this.mTrim = trim;

+        float minx = delta + trim[0] * (v.mDimx - delta);

+        float miny = delta + trim[1] * (v.mDimy - delta);

+        float minz = delta + trim[2] * (v.mDimz - delta);

+        float maxx = v.mDimx - delta - trim[3] * (v.mDimx - delta);

+        float maxy = v.mDimy - delta - trim[4] * (v.mDimy - delta);

+        float maxz = v.mDimz - delta - trim[5] * (v.mDimz - delta);

+        mVert = new float[]{

+                minx, miny, minz,

+                maxx, miny, minz,

+                maxx, maxy, minz,

+                minx, maxy, minz,

+                minx, miny, maxz,

+                maxx, miny, maxz,

+                maxx, maxy, maxz,

+                minx, maxy, maxz,

+        };

+    }

+

+    public void clone(Cube src) {

+        System.arraycopy(src.mTrim, 0, mTrim, 0, mTrim.length);

+        mVert = Arrays.copyOf(src.mVert, src.mVert.length);

+        mIndex = Arrays.copyOf(src.mIndex, src.mIndex.length);

+    }

+

+    public float[] getTrim() {

+        return mTrim;

+    }

+

+    @Override

+    public String toString() {

+        return "CUBE[" + fs(mVert, 0, 3) + "][" + fs(mVert, 18, 3) + "]";

+    }

+

+    private static String fs(float[] f, int off, int n) {

+        DecimalFormat df = new DecimalFormat("##0.000");

+        String ret = "";

+        for (int i = off; i < off + n; i++) {

+            String s = "       " + df.format(f[i]);

+

+            if (i != off) {

+                ret += ",";

+            }

+            ret += s.substring(s.length() - 8);

+

+        }

+        return ret;

+    }

+

+    public Cube() {

+        mVert = new float[]{

+                -1.f, -1.f, -1.f,

+                1.f, -1.f, -1.f,

+                1.f, 1.f, -1.f,

+                -1.f, 1.f, -1.f,

+                -1.f, -1.f, 1.f,

+                1.f, -1.f, 1.f,

+                1.f, 1.f, 1.f,

+                -1.f, 1.f, 1.f,

+        };

+

+        mIndex = new int[]{

+                2, 1, 0,

+                0, 3, 2,

+                7, 4, 5,

+                5, 6, 7,

+                1, 2, 6,

+                6, 5, 1,

+                4, 7, 3,

+                3, 0, 4,

+                2, 3, 7,

+                7, 6, 2,

+                0, 1, 5,

+                5, 4, 0

+        };

+        for (int i = 0; i < mIndex.length; i++) {

+            mIndex[i] *= 3;

+        }

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Material.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Material.java
new file mode 100644
index 0000000..8517754
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Material.java
@@ -0,0 +1,322 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package com.example.android.rs.vr.engine;

+

+

+import android.graphics.Color;

+import android.renderscript.Allocation;

+import android.renderscript.Element;

+import android.renderscript.RenderScript;

+import android.renderscript.Type;

+

+/**

+ * Defines the material properties of a pixel value

+ * RGB, Opacity diffuse specular, ambient

+ */

+public class Material {

+    public static final int SIZE = 64 * 1024;

+    public static final int STRIDE = 8;

+

+    Allocation mOpacityAllocation;

+    Allocation mColorMapAllocation;

+

+    public byte[] mOpacityTable = new byte[SIZE];

+    MaterialProp[] mMaterialProp = new MaterialProp[0];

+    public byte[] mColor = new byte[SIZE * STRIDE]; // table contain r, g, b, A, S, D

+    public static final int RED = 0;

+    public static final int GREEN = 1;

+    public static final int BLUE = 2;

+    public static final int DIFF = 4;

+    public static final int SPEC = 5;

+    public static final int AMB = 6;

+

+    public static class MaterialProp {

+        int mPos;

+        int mRed;

+        int mGreen;

+        int mBlue;

+        float mDiffuse;

+        float mSpecular;

+        float mAmbient;

+    }

+

+    /**

+     * Clamp limits to less than or equal to 255

+     * this is done with a very efficient bit fiddling trick

+     * @param c value being clamped

+     * @return values in the range 0-255

+     */

+    private static int clamp(int c) {

+        final int N = 255; // the value clamp is limiting to

+        c &= ~(c >> 31);

+        c -= N;

+        c &= (c >> 31);

+        c += N;

+        return c;

+    }

+

+    public static class Opactiy {

+        int mPos;

+        float mValue;

+    }

+

+    public Opactiy[] mOpacity = new Opactiy[0];

+

+    public Material() {

+        simpleSetup(1150, 1300);

+    }

+

+    public void simpleSetup(int start, int end) {

+        float diffuse = .7f;

+        float specular = .0f;

+        float ambient = .3f;

+        for (int i = Short.MIN_VALUE; i < Short.MAX_VALUE; i++) {

+            int off = i & 0xFFFF;

+            int p = STRIDE * (off);

+            byte v = (byte) clamp((int) (255 * (i - start) / (end - start)));

+            mColor[p + RED] = v;

+            mColor[p + GREEN] = v;

+            mColor[p + BLUE] = v;

+            mColor[p + DIFF] = (byte) (255 * diffuse);

+            mColor[p + SPEC] = (byte) (255 * specular);

+            mColor[p + AMB] = (byte) (255 * ambient);

+            mOpacityTable[off] = v;

+        }

+    }

+

+    public void setup(int[] means, int start, int end) {

+        int[] pos = new int[means.length - 1];

+        int[] red = new int[means.length - 1];

+        int[] green = new int[means.length - 1];

+        int[] blue = new int[means.length - 1];

+

+        for (int i = 0; i < pos.length; i++) {

+            pos[i] = (means[i] + means[i + 1]) / 2;

+            float f = i / (float) (pos.length - 1);

+            float h = 1 - f * f * f;

+            float s = (float) (1 / (1 + Math.exp((f - .5) * 5)));

+            int rgb = Color.HSVToColor(new float[]{360 * h, s, f});

+            red[i] = (rgb >> 16) & 0xff;

+            green[i] = (rgb >> 8) & 0xff;

+            blue[i] = (rgb >> 0) & 0xff;

+        }

+        mMaterialProp = new MaterialProp[pos.length];

+

+        float diffuse = .7f;

+        float specular = .0f;

+        float ambient = .3f;

+        for (int i = 0; i < pos.length; i++) {

+            mMaterialProp[i] = new MaterialProp();

+            mMaterialProp[i].mAmbient = ambient;

+            mMaterialProp[i].mSpecular = specular;

+            mMaterialProp[i].mDiffuse = diffuse;

+            mMaterialProp[i].mPos = pos[i];

+            float t = i / (float) (pos.length - 1);

+

+            mMaterialProp[i].mRed = red[i];

+            mMaterialProp[i].mGreen = green[i];

+            mMaterialProp[i].mBlue = blue[i];

+

+        }

+        mOpacity = new Opactiy[2];

+        mOpacity[0] = new Opactiy();

+        mOpacity[0].mPos = start;

+        mOpacity[0].mValue = 0;

+

+        mOpacity[1] = new Opactiy();

+        mOpacity[1].mPos = end;

+        mOpacity[1].mValue = 1;

+

+        buildOpacityTable();

+        buildMaterialProp();

+    }

+

+    public void setup(int[][] opacity, int[][] material) {

+        mMaterialProp = new MaterialProp[material.length];

+

+        for (int i = 0; i < material.length; i++) {

+            int rgb = material[i][1] & 0xFFFFFF;

+

+            float ambient = (material[i].length > 2) ? material[i][2] / 100.f : .2f;

+            float diffuse = (material[i].length > 3) ? material[i][3] / 100.f : .6f;

+            float specular = (material[i].length > 4) ? material[i][4] / 100.f : .2f;

+

+            mMaterialProp[i] = new MaterialProp();

+            mMaterialProp[i].mAmbient = ambient;

+            mMaterialProp[i].mSpecular = specular;

+            mMaterialProp[i].mDiffuse = diffuse;

+            mMaterialProp[i].mRed = (rgb >> 16) & 0xff;

+            mMaterialProp[i].mGreen = (rgb >> 8) & 0xff;

+            mMaterialProp[i].mBlue = (rgb >> 0) & 0xff;

+

+            mMaterialProp[i].mPos = material[i][0];

+

+        }

+        mOpacity = new Opactiy[opacity.length];

+        for (int i = 0; i < opacity.length; i++) {

+            mOpacity[i] = new Opactiy();

+            mOpacity[i].mPos = opacity[i][0];

+            mOpacity[i].mValue = opacity[i][1] / 255.f;

+        }

+        buildOpacityTable();

+        buildMaterialProp();

+    }

+

+    public void setup(int start, int end) {

+        int[] pos = {1050, 1140, 1200, 1210, 1231};

+

+        mMaterialProp = new MaterialProp[pos.length];

+

+        float diffuse = .7f;

+        float specular = .0f;

+        float ambient = .3f;

+        for (int i = 0; i < pos.length; i++) {

+            mMaterialProp[i] = new MaterialProp();

+            mMaterialProp[i].mAmbient = ambient;

+            mMaterialProp[i].mSpecular = specular;

+            mMaterialProp[i].mDiffuse = diffuse;

+            mMaterialProp[i].mPos = pos[i];

+            float t = i / (float) (pos.length - 1);

+            int rgb = (int) (Math.random() * 0xFFFFFF);

+            mMaterialProp[i].mRed = (rgb >> 16) & 0xff;

+            mMaterialProp[i].mGreen = (rgb >> 8) & 0xff;

+            mMaterialProp[i].mBlue = (rgb >> 0) & 0xff;

+        }

+        mOpacity = new Opactiy[2];

+        mOpacity[0] = new Opactiy();

+        mOpacity[0].mPos = start;

+        mOpacity[0].mValue = 0;

+

+        mOpacity[1] = new Opactiy();

+        mOpacity[1].mPos = end;

+        mOpacity[1].mValue = 1;

+

+        buildOpacityTable();

+        buildMaterialProp();

+    }

+

+    void buildOpacityTable() {

+        if (mOpacity.length == 0) {

+            return;

+        }

+        for (int i = Short.MIN_VALUE; i <= mOpacity[0].mPos; i++) {

+            int p = i & 0xFFFF;

+            mOpacityTable[p] = (byte) (mOpacity[0].mValue * 255);

+        }

+        for (int k = 0; k < mOpacity.length - 1; k++) {

+            for (int i = mOpacity[k].mPos; i < mOpacity[k + 1].mPos; i++) {

+                int p = i & 0xFFFF;

+                float dist = mOpacity[k + 1].mPos - mOpacity[k].mPos;

+                float t = (i - mOpacity[k].mPos) / dist;

+                float v = mOpacity[k].mValue * (1 - t) + t * mOpacity[k + 1].mValue;

+                mOpacityTable[p] = (byte) (v * 255);

+            }

+        }

+        int last = mOpacity.length - 1;

+        for (int i = mOpacity[last].mPos; i <= Short.MAX_VALUE; i++) {

+            int p = i & 0xFFFF;

+            mOpacityTable[p] = (byte) (mOpacity[last].mValue * 255);

+

+        }

+    }

+

+    public void buildMaterialProp() {

+        MaterialProp[] m = mMaterialProp;

+        if (m.length == 0) {

+            return;

+        }

+        {

+            MaterialProp mp = m[0];

+            int red = m[0].mRed;

+            int green = m[0].mGreen;

+            int blue = m[0].mBlue;

+

+            for (int i = Short.MIN_VALUE; i <= m[0].mPos; i++) {

+                int p = STRIDE * (i & 0xFFFF);

+                mColor[p + RED] = (byte) red;

+                mColor[p + GREEN] = (byte) green;

+                mColor[p + BLUE] = (byte) blue;

+

+                mColor[p + DIFF] = (byte) (255 * mp.mDiffuse);

+                mColor[p + SPEC] = (byte) (255 * mp.mSpecular);

+                mColor[p + AMB] = (byte) (255 * mp.mAmbient);

+            }

+        }

+        for (int k = 0; k < m.length - 1; k++) {

+            for (int i = m[k].mPos; i < m[k + 1].mPos; i++) {

+                int p = STRIDE * (i & 0xFFFF);

+                float dist = m[k + 1].mPos - m[k].mPos;

+                float t2 = (i - m[k].mPos) / dist;

+                float t1 = 1 - t2;

+

+

+                int red = (int) (m[k].mRed * t1 + m[k + 1].mRed * t2);

+                int green = (int) (m[k].mGreen * t1 + m[k + 1].mGreen * t2);

+                int blue = (int) (m[k].mBlue * t1 + m[k + 1].mBlue * t2);

+

+                float diffuse = m[k].mDiffuse * t1 + m[k + 1].mDiffuse * t2;

+                float specular = m[k].mSpecular * t1 + m[k + 1].mSpecular * t2;

+                float ambient = m[k].mAmbient * t1 + m[k + 1].mAmbient * t2;

+

+

+                mColor[p + RED] = (byte) red;

+                mColor[p + GREEN] = (byte) green;

+                mColor[p + BLUE] = (byte) blue;

+

+                mColor[p + DIFF] = (byte) (255 * diffuse);

+                mColor[p + SPEC] = (byte) (255 * specular);

+                mColor[p + AMB] = (byte) (255 * ambient);

+            }

+        }

+        {

+            int last = m.length - 1;

+            MaterialProp mp = m[last];

+            int red = mp.mRed;

+            int green = mp.mGreen;

+            int blue = mp.mBlue;

+            for (int i = mp.mPos; i <= Short.MAX_VALUE; i++) {

+                int p = STRIDE * (i & 0xFFFF);

+                mColor[p + RED] = (byte) red;

+                mColor[p + GREEN] = (byte) green;

+                mColor[p + BLUE] = (byte) blue;

+

+                mColor[p + DIFF] = (byte) (255 * mp.mDiffuse);

+                mColor[p + SPEC] = (byte) (255 * mp.mSpecular);

+                mColor[p + AMB] = (byte) (255 * mp.mAmbient);

+            }

+        }

+    }

+

+    public Allocation getOpacityAllocation(RenderScript rs) {

+        if (mOpacityAllocation == null) {

+            Type.Builder b = new Type.Builder(rs, Element.U8(rs));

+            b.setX(mOpacityTable.length);

+            mOpacityAllocation = Allocation.createTyped(rs, b.create());

+        }

+        mOpacityAllocation.copyFromUnchecked(mOpacityTable);

+        return mOpacityAllocation;

+    }

+

+    public Allocation getColorMapAllocation(RenderScript rs) {

+        if (mColorMapAllocation == null) {

+            Type.Builder b = new Type.Builder(rs, Element.U8_4(rs));

+            b.setX(mColor.length / 4);

+            mColorMapAllocation = Allocation.createTyped(rs, b.create());

+        }

+        mColorMapAllocation.copyFromUnchecked(mColor);

+        return mColorMapAllocation;

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Matrix.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Matrix.java
new file mode 100644
index 0000000..e178aee
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Matrix.java
@@ -0,0 +1,368 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package com.example.android.rs.vr.engine;

+

+import android.util.Log;

+

+import java.text.*;

+import java.util.*;

+

+/**

+ * Generic matrix code

+ * Written for maximum portability between desktop and Android

+ * Not in performance critical sections

+ */

+public class Matrix {

+

+    private static final String LOGTAG = "Matrix";

+    public double[] m;

+

+    public void makeRotation() {

+        {

+            double[] v = {m[0], m[4], m[8]};

+            VectorUtil.normalize(v);

+            m[0] = v[0];

+            m[4] = v[1];

+            m[8] = v[2];

+        }

+        {

+            double[] v = {m[1], m[5], m[9]};

+            VectorUtil.normalize(v);

+            m[1] = v[0];

+            m[5] = v[1];

+            m[9] = v[2];

+        }

+        {

+            double[] v = {m[2], m[6], m[10]};

+            VectorUtil.normalize(v);

+            m[2] = v[0];

+            m[6] = v[1];

+            m[10] = v[2];

+        }

+

+    }

+

+    private static String trim(String s) {

+        return s.substring(s.length() - 7);

+    }

+

+    public void print() {

+        DecimalFormat df = new DecimalFormat("      ##0.000");

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

+            String s="";

+            for (int j = 0; j < 4; j++) {

+               s+= ((j == 0) ? "[ " : " , ") + trim("       "+df.format(m[i * 4 + j]));

+            }

+            Log.v(LOGTAG, s+"]");

+        }

+    }

+

+    public Matrix() {

+        m = new double[4 * 4];

+        setToUnit();

+    }

+

+    public Matrix(Matrix matrix) {

+        this(Arrays.copyOf(matrix.m, matrix.m.length));

+    }

+

+    protected Matrix(double[] m) {

+        this.m = m;

+    }

+

+    public void setToUnit() {

+        for (int i = 1; i < m.length; i++) {

+            m[i] = 0;

+        }

+        m[0] = 1;

+        m[5] = 1;

+        m[10] = 1;

+        m[15] = 1;

+    }

+

+    public void mult4(float[] src, float[] dest) {

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

+            int col = i * 4;

+            double sum = 0;

+            for (int j = 0; j < 4; j++) {

+                sum += m[col + j] * src[j];

+            }

+            dest[i] = (float) sum;

+        }

+    }

+

+    public void mult3(float[] src, float[] dest) {

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

+            int col = i * 4;

+            double sum = m[col + 3];

+            for (int j = 0; j < 3; j++) {

+                sum += m[col + j] * src[j];

+            }

+            dest[i] = (float) sum;

+        }

+    }

+

+    public void mult3v(float[] src, float[] dest) {

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

+            int col = i * 4;

+            double sum = 0;

+            for (int j = 0; j < 3; j++) {

+                sum += m[col + j] * src[j];

+            }

+            dest[i] = (float) sum;

+        }

+    }

+

+    public void mult4(double[] src, double[] dest) {

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

+            int col = i * 4;

+            double sum = 0;

+            for (int j = 0; j < 4; j++) {

+                sum += m[col + j] * src[j];

+            }

+            dest[i] = (float) sum;

+        }

+    }

+

+    public void mult3(double[] src, double[] dest) {

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

+            int col = i * 4;

+            double sum = m[col + 3];

+            for (int j = 0; j < 3; j++) {

+                sum += m[col + j] * src[j];

+            }

+            dest[i] = (float) sum;

+        }

+    }

+

+    public void mult3v(double[] src, double[] dest) {

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

+            int col = i * 4;

+            double sum = 0;

+            for (int j = 0; j < 3; j++) {

+                sum += m[col + j] * src[j];

+            }

+            dest[i] = (float) sum;

+        }

+    }

+

+    public double[] vecmult(double[] src) {

+        double[] ret = new double[3];

+        mult3v(src, ret);

+        return ret;

+    }

+

+    public void mult3(float[] src, int off1, float[] dest, int off2) {

+

+        int col = 0 * 4;

+        double sum = m[col + 3];

+        for (int j = 0; j < 3; j++) {

+            sum += m[col + j] * src[j + off1];

+        }

+        float v0 = (float) sum;

+

+

+        col = 1 * 4;

+        sum = m[col + 3];

+        for (int j = 0; j < 3; j++) {

+            sum += m[col + j] * src[j + off1];

+        }

+

+        float v1 = (float) sum;

+

+

+        col = 2 * 4;

+        sum = m[col + 3];

+        for (int j = 0; j < 3; j++) {

+            sum += m[col + j] * src[j + off1];

+        }

+        float v2 = (float) sum;

+

+        dest[off2] = v0;

+        dest[1 + off2] = v1;

+        dest[2 + off2] = v2;

+

+    }

+

+    public Matrix invers() {

+        double[] inv = new double[16];

+

+        inv[0] = m[5] * m[10] * m[15] -

+                m[5] * m[11] * m[14] -

+                m[9] * m[6] * m[15] +

+                m[9] * m[7] * m[14] +

+                m[13] * m[6] * m[11] -

+                m[13] * m[7] * m[10];

+

+        inv[4] = -m[4] * m[10] * m[15] +

+                m[4] * m[11] * m[14] +

+                m[8] * m[6] * m[15] -

+                m[8] * m[7] * m[14] -

+                m[12] * m[6] * m[11] +

+                m[12] * m[7] * m[10];

+

+        inv[8] = m[4] * m[9] * m[15] -

+                m[4] * m[11] * m[13] -

+                m[8] * m[5] * m[15] +

+                m[8] * m[7] * m[13] +

+                m[12] * m[5] * m[11] -

+                m[12] * m[7] * m[9];

+

+        inv[12] = -m[4] * m[9] * m[14] +

+                m[4] * m[10] * m[13] +

+                m[8] * m[5] * m[14] -

+                m[8] * m[6] * m[13] -

+                m[12] * m[5] * m[10] +

+                m[12] * m[6] * m[9];

+

+        inv[1] = -m[1] * m[10] * m[15] +

+                m[1] * m[11] * m[14] +

+                m[9] * m[2] * m[15] -

+                m[9] * m[3] * m[14] -

+                m[13] * m[2] * m[11] +

+                m[13] * m[3] * m[10];

+

+        inv[5] = m[0] * m[10] * m[15] -

+                m[0] * m[11] * m[14] -

+                m[8] * m[2] * m[15] +

+                m[8] * m[3] * m[14] +

+                m[12] * m[2] * m[11] -

+                m[12] * m[3] * m[10];

+

+        inv[9] = -m[0] * m[9] * m[15] +

+                m[0] * m[11] * m[13] +

+                m[8] * m[1] * m[15] -

+                m[8] * m[3] * m[13] -

+                m[12] * m[1] * m[11] +

+                m[12] * m[3] * m[9];

+

+        inv[13] = m[0] * m[9] * m[14] -

+                m[0] * m[10] * m[13] -

+                m[8] * m[1] * m[14] +

+                m[8] * m[2] * m[13] +

+                m[12] * m[1] * m[10] -

+                m[12] * m[2] * m[9];

+

+        inv[2] = m[1] * m[6] * m[15] -

+                m[1] * m[7] * m[14] -

+                m[5] * m[2] * m[15] +

+                m[5] * m[3] * m[14] +

+                m[13] * m[2] * m[7] -

+                m[13] * m[3] * m[6];

+

+        inv[6] = -m[0] * m[6] * m[15] +

+                m[0] * m[7] * m[14] +

+                m[4] * m[2] * m[15] -

+                m[4] * m[3] * m[14] -

+                m[12] * m[2] * m[7] +

+                m[12] * m[3] * m[6];

+

+        inv[10] = m[0] * m[5] * m[15] -

+                m[0] * m[7] * m[13] -

+                m[4] * m[1] * m[15] +

+                m[4] * m[3] * m[13] +

+                m[12] * m[1] * m[7] -

+                m[12] * m[3] * m[5];

+

+        inv[14] = -m[0] * m[5] * m[14] +

+                m[0] * m[6] * m[13] +

+                m[4] * m[1] * m[14] -

+                m[4] * m[2] * m[13] -

+                m[12] * m[1] * m[6] +

+                m[12] * m[2] * m[5];

+

+        inv[3] = -m[1] * m[6] * m[11] +

+                m[1] * m[7] * m[10] +

+                m[5] * m[2] * m[11] -

+                m[5] * m[3] * m[10] -

+                m[9] * m[2] * m[7] +

+                m[9] * m[3] * m[6];

+

+        inv[7] = m[0] * m[6] * m[11] -

+                m[0] * m[7] * m[10] -

+                m[4] * m[2] * m[11] +

+                m[4] * m[3] * m[10] +

+                m[8] * m[2] * m[7] -

+                m[8] * m[3] * m[6];

+

+        inv[11] = -m[0] * m[5] * m[11] +

+                m[0] * m[7] * m[9] +

+                m[4] * m[1] * m[11] -

+                m[4] * m[3] * m[9] -

+                m[8] * m[1] * m[7] +

+                m[8] * m[3] * m[5];

+

+        inv[15] = m[0] * m[5] * m[10] -

+                m[0] * m[6] * m[9] -

+                m[4] * m[1] * m[10] +

+                m[4] * m[2] * m[9] +

+                m[8] * m[1] * m[6] -

+                m[8] * m[2] * m[5];

+

+

+        double det;

+        det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];

+

+        if (det == 0) {

+            return null;

+        }

+

+        det = 1.0 / det;

+

+        double[] out = new double[16];

+

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

+            out[i] = inv[i] * det;

+        }

+

+        Matrix ret = new Matrix(out);

+        return ret;

+    }

+

+    public void getAsFloats(float[] fm) {

+        for (int y = 0; y < 4; y++) {

+            int col = y * 4;

+

+            for (int x = 0; x < 4; x++)

+                fm[y + x * 4] = (float) m[x + y * 4];

+        }

+    }

+

+    Matrix mult(Matrix b) {

+        return new Matrix(multiply(this.m, b.m));

+    }

+

+    Matrix premult(Matrix b) {

+        return new Matrix(multiply(b.m, this.m));

+    }

+

+    private static double[] multiply(double a[], double b[]) {

+        double[] resultant = new double[16];

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

+            for (int j = 0; j < 4; j++) {

+                for (int k = 0; k < 4; k++) {

+                    resultant[i + 4 * j] += a[i + 4 * k] * b[k + 4 * j];

+                }

+            }

+        }

+

+        return resultant;

+    }

+

+    public void clone(Matrix src) {

+        System.arraycopy(src.m, 0, m, 0, m.length);

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Pipeline.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Pipeline.java
new file mode 100644
index 0000000..92546e0
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Pipeline.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.rs.vr.engine;
+
+/**
+ * The interface to the vr rendering pipeline
+ * This defines the various rendering stages that the system goes through
+ */
+public interface Pipeline {
+    /**
+     * cancel the current execution
+     */
+    public void cancel();
+
+    /**
+     * @return true if the rendering was canceled the output is not valid if true
+     */
+    public boolean isCancel();
+
+    /**
+     * The pipline uses this stage to setup buffers it needs
+     * @param state
+     */
+    public void initBuffers(VrState state);
+
+    /**
+     * This stage does needed transformations on the triangles to screen space
+     * @param state
+     */
+    public void setupTriangles(VrState state);
+
+    /**
+     * This stage converts triangles into buffers used in the raycast
+     * @param state
+     */
+    public void rasterizeTriangles(VrState state);
+
+    /**
+     * This stage generates the final image
+     * @param state
+     */
+    public void raycast(VrState state);
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Quaternion.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Quaternion.java
new file mode 100644
index 0000000..bc0a0ae
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Quaternion.java
@@ -0,0 +1,161 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package com.example.android.rs.vr.engine;

+

+/**

+ * Generic Quaternion

+ * Written for maximum portability between desktop and Android

+ * Not in performance critical sections

+ */

+public class Quaternion {

+    private final double[] x = new double[4]; // w,x,y,z,

+

+    public void set(double w, double x, double y, double z) {

+        this.x[0] = w;

+        this.x[1] = x;

+        this.x[2] = y;

+        this.x[3] = z;

+    }

+

+    public void clone(Quaternion src) {

+        System.arraycopy(src.x, 0, x, 0, x.length);

+    }

+

+    private static double[] cross(double[] a, double[] b) {

+        double out0 = a[1] * b[2] - b[1] * a[2];

+        double out1 = a[2] * b[0] - b[2] * a[0];

+        double out2 = a[0] * b[1] - b[0] * a[1];

+        return new double[]{out0, out1, out2};

+    }

+

+    private static double dot(double[] a, double[] b) {

+        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];

+    }

+

+    private static double[] normal(double[] a) {

+        double norm = Math.sqrt(dot(a, a));

+        return new double[]{a[0] / norm, a[1] / norm, a[2] / norm};

+    }

+

+    public void set(double[] v1, double[] v2) {

+        double[] vec1 = normal(v1);

+        double[] vec2 = normal(v2);

+        double[] axis = normal(cross(vec1, vec2));

+        double angle = Math.acos(dot(vec1, vec2));

+        set(angle, axis);

+    }

+

+    public static double calcAngle(double[] v1, double[] v2) {

+        double[] vec1 = normal(v1);

+        double[] vec2 = normal(v2);

+        return Math.acos(Math.min(dot(vec1, vec2), 1));

+    }

+

+    public static double[] calcAxis(double[] v1, double[] v2) {

+        double[] vec1 = normal(v1);

+        double[] vec2 = normal(v2);

+        return normal(cross(vec1, vec2));

+    }

+

+    public void set(double angle, double[] axis) {

+        x[0] = Math.cos(angle / 2);

+        double sin = Math.sin(angle / 2);

+        x[1] = axis[0] * sin;

+        x[2] = axis[1] * sin;

+        x[3] = axis[2] * sin;

+    }

+

+    public Quaternion(double x0, double x1, double x2, double x3) {

+        x[0] = x0;

+        x[1] = x1;

+        x[2] = x2;

+        x[3] = x3;

+    }

+

+    public Quaternion conjugate() {

+        return new Quaternion(x[0], -x[1], -x[2], -x[3]);

+    }

+

+    public Quaternion plus(Quaternion b) {

+        Quaternion a = this;

+        return new Quaternion(a.x[0] + b.x[0], a.x[1] + b.x[1], a.x[2] + b.x[2], a.x[3] + b.x[3]);

+    }

+

+    public Quaternion times(Quaternion b) {

+        Quaternion a = this;

+        double y0 = a.x[0] * b.x[0] - a.x[1] * b.x[1] - a.x[2] * b.x[2] - a.x[3] * b.x[3];

+        double y1 = a.x[0] * b.x[1] + a.x[1] * b.x[0] + a.x[2] * b.x[3] - a.x[3] * b.x[2];

+        double y2 = a.x[0] * b.x[2] - a.x[1] * b.x[3] + a.x[2] * b.x[0] + a.x[3] * b.x[1];

+        double y3 = a.x[0] * b.x[3] + a.x[1] * b.x[2] - a.x[2] * b.x[1] + a.x[3] * b.x[0];

+        return new Quaternion(y0, y1, y2, y3);

+    }

+

+    public Quaternion inverse() {

+        double d = x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3];

+        return new Quaternion(x[0] / d, -x[1] / d, -x[2] / d, -x[3] / d);

+    }

+

+    public Quaternion divides(Quaternion b) {

+        Quaternion a = this;

+        return a.inverse().times(b);

+    }

+

+

+    public double[] rotateVec(double[] v) {

+

+        double v0 = v[0];

+        double v1 = v[1];

+        double v2 = v[2];

+

+        double s = x[1] * v0 + x[2] * v1 + x[3] * v2;

+

+        double n0 = 2 * (x[0] * (v0 * x[0] - (x[2] * v2 - x[3] * v1)) + s * x[1]) - v0;

+        double n1 = 2 * (x[0] * (v1 * x[0] - (x[3] * v0 - x[1] * v2)) + s * x[2]) - v1;

+        double n2 = 2 * (x[0] * (v2 * x[0] - (x[1] * v1 - x[2] * v0)) + s * x[3]) - v2;

+

+        return new double[]{n0, n1, n2};

+

+    }

+

+    void matrix() {

+        double xx = x[1] * x[1];

+        double xy = x[1] * x[2];

+        double xz = x[1] * x[3];

+        double xw = x[1] * x[0];

+

+        double yy = x[2] * x[2];

+        double yz = x[2] * x[3];

+        double yw = x[2] * x[0];

+

+        double zz = x[3] * x[3];

+        double zw = x[3] * x[0];

+        double[] m = new double[16];

+        m[0] = 1 - 2 * (yy + zz);

+        m[1] = 2 * (xy - zw);

+        m[2] = 2 * (xz + yw);

+

+        m[4] = 2 * (xy + zw);

+        m[5] = 1 - 2 * (xx + zz);

+        m[6] = 2 * (yz - xw);

+

+        m[8] = 2 * (xz - yw);

+        m[9] = 2 * (yz + xw);

+        m[10] = 1 - 2 * (xx + yy);

+

+        m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0;

+        m[15] = 1;

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/RsBrickedBitMask.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/RsBrickedBitMask.java
new file mode 100644
index 0000000..a9495ce
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/RsBrickedBitMask.java
@@ -0,0 +1,97 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package com.example.android.rs.vr.engine;

+

+import android.renderscript.Allocation;

+import android.renderscript.RenderScript;

+import android.renderscript.Type;

+import android.util.Log;

+

+/**

+ * create bricked binary representation of the non transparent voxels

+ */

+public class RsBrickedBitMask {

+    private static final String LOGTAG = "BrickedBitMask";

+    ScriptC_bricked scriptC_bricked;

+

+    int mDimX;

+    int mDimY;

+    int mDimZ;

+    int m_bricks_dimx;

+    int m_bricks_dimy;

+    int m_bricks_dimz;

+    Volume mVolume;

+    int mBrickCnt = 0;

+

+    Allocation mBrick_allocation;

+

+    public final static int BSIZE = 32;

+

+    public static final byte TYPE_BYTE = 1;

+    public static final byte TYPE_SHORT = 1;

+    public static final byte TYPE_INT = 2;

+

+    public RsBrickedBitMask(VrState state) {

+

+        mVolume = state.mVolume;

+        mDimX = mVolume.mDimx;

+        mDimY = mVolume.mDimy;

+        mDimZ = mVolume.mDimz;

+        m_bricks_dimx = (mDimX + 31) / 32;

+        m_bricks_dimy = (mDimY + 31) / 32;

+        m_bricks_dimz = (mDimZ + 31) / 32;

+        int maxBrick = m_bricks_dimx * m_bricks_dimy * m_bricks_dimz;

+        int size = maxBrick * 32 * 32; // divide by 4 because we will try U32_4

+

+        Type.Builder b = new Type.Builder(state.mRs, android.renderscript.Element.U32(state.mRs));

+        b.setX(size);

+        mBrick_allocation = Allocation.createTyped(state.mRs, b.create(), Allocation.USAGE_SCRIPT);

+

+        scriptC_bricked = new ScriptC_bricked(state.mRs);

+

+        scriptC_bricked.set_volume(mVolume.mVolumeAllocation);

+        scriptC_bricked.set_brick_dimx(m_bricks_dimx);

+        scriptC_bricked.set_brick_dimy(m_bricks_dimy);

+        scriptC_bricked.set_brick_dimz(m_bricks_dimz);

+        scriptC_bricked.set_opacity(state.mMaterial.getOpacityAllocation(state.mRs));

+        state.mRs.finish();

+

+        scriptC_bricked.forEach_pack_chunk(mBrick_allocation);

+

+        Allocation tmp = Allocation.createTyped(state.mRs, b.create(), Allocation.USAGE_SCRIPT);

+        scriptC_bricked.set_bricks(mBrick_allocation);

+        scriptC_bricked.forEach_dilate(mBrick_allocation, tmp);

+

+        mBrick_allocation.destroy();

+        mBrick_allocation = tmp;

+    }

+

+    Allocation createChunkAllocation(RenderScript rs) {

+        Type.Builder b = new Type.Builder(rs, android.renderscript.Element.U32(rs));

+        b.setX(mDimX / 8);

+        b.setY(mDimY);

+        b.setZ(32);

+        return Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);

+    }

+

+    Allocation createBitChunkAllocation(RenderScript rs) {

+        Type.Builder b = new Type.Builder(rs, android.renderscript.Element.U8(rs));

+        b.setX(mDimX / 8);

+        b.setY(mDimY);

+        b.setZ(32);

+        return Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Transform.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Transform.java
new file mode 100644
index 0000000..5dc5fc3
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Transform.java
@@ -0,0 +1,208 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.example.android.rs.vr.engine;

+

+import android.util.Log;

+

+import java.text.DecimalFormat;

+

+/**

+ * code to manage transformations between world screen and volume space.

+ */

+public class Transform {

+    public final static char WORLD_SPACE = 0;

+    public final static char SCREEN_SPACE = 1;

+    public final static char VOLUME_SPACE = 2;

+    private static final String LOGTAG = "Transform";

+

+    Matrix[][] mAllMat = new Matrix[3][3];

+    public ViewMatrix mViewMatrix = new ViewMatrix();

+    float[] mVoxelDim = new float[3];

+

+    public void clone(Transform src) {

+        System.arraycopy(src.mVoxelDim, 0, mVoxelDim, 0, mVoxelDim.length);

+        mViewMatrix.clone(src.mViewMatrix);

+        updateAllMatrix();

+    }

+

+    public void updateAllMatrix() {

+        mViewMatrix.calcMatrix();

+        Matrix m = new Matrix();

+        m.setToUnit();

+        mAllMat[0][0] = m;

+        mAllMat[1][1] = m;

+        mAllMat[2][2] = m;

+        mAllMat[SCREEN_SPACE][WORLD_SPACE] = new Matrix(mViewMatrix);

+        mAllMat[WORLD_SPACE][SCREEN_SPACE] = mViewMatrix.invers();

+        m = new Matrix();

+        m.setToUnit();

+        if (mVoxelDim[0] > 0) {

+            int min = 0;

+

+            m.m[0] = 1 / mVoxelDim[0];

+            m.m[5] = 1 / mVoxelDim[1];

+            m.m[10] = 1 / mVoxelDim[2];

+        }

+        mAllMat[WORLD_SPACE][VOLUME_SPACE] = m;

+        mAllMat[VOLUME_SPACE][WORLD_SPACE] = m.invers();

+        mAllMat[SCREEN_SPACE][VOLUME_SPACE] = m = m.premult(mViewMatrix);

+        mAllMat[VOLUME_SPACE][SCREEN_SPACE] = m.invers();

+

+    }

+

+    public void setVoxelDim(float[] volDim) {

+        mVoxelDim[0] = volDim[0];

+        mVoxelDim[1] = volDim[1];

+        mVoxelDim[2] = volDim[2];

+    }

+

+    public Matrix getMatrix(char from, char to) {

+        return mAllMat[from][to];

+    }

+

+    public void setScreenDim(int x, int y) {

+        mViewMatrix.setScreenDim(x, y);

+        updateAllMatrix();

+    }

+

+    public double[] getLookPoint() {

+        return mViewMatrix.getLookPoint();

+    }

+

+    public void setLookPoint(double[] mLookPoint) {

+        mViewMatrix.setLookPoint(mLookPoint);

+        updateAllMatrix();

+    }

+

+    public double[] getEyePoint() {

+        return mViewMatrix.getEyePoint();

+    }

+

+    public void setEyePoint(double[] mEyePoint) {

+        mViewMatrix.setEyePoint(mEyePoint);

+        updateAllMatrix();

+    }

+

+    public double[] getUpVector() {

+        return mViewMatrix.getUpVector();

+    }

+

+    public void setUpVector(double[] mUpVector) {

+        mViewMatrix.setUpVector(mUpVector);

+        updateAllMatrix();

+    }

+

+    public double getScreenWidth() {

+        return mViewMatrix.getScreenWidth();

+    }

+

+    public void setScreenWidth(double screenWidth) {

+        mViewMatrix.setScreenWidth(screenWidth);

+        updateAllMatrix();

+    }

+

+    public void lookAt(TriData tri, int w, int h) {

+        mViewMatrix.lookAt(tri, mVoxelDim, w, h);

+        updateAllMatrix();

+    }

+

+    public void look(char dir, TriData tri, int w, int h) {

+        mViewMatrix.look(dir, tri, mVoxelDim, w, h);

+        updateAllMatrix();

+    }

+

+    public void trackBallUp(float x, float y) {

+        mViewMatrix.trackBallUP(x, y);

+        updateAllMatrix();

+    }

+

+    public void trackBallDown(float x, float y) {

+        mViewMatrix.trackBallDown(x, y);

+    }

+

+    public void trackBallMove(float x, float y) {

+        mViewMatrix.trackBallMove(x, y);

+        updateAllMatrix();

+    }

+

+    static DecimalFormat df = new DecimalFormat("      ##0.000");

+

+    private static String trim(double d) {

+        String s = df.format(d);

+        return s.substring(s.length() - 6);

+    }

+

+    public static void print(float[] d) {

+        String s = "";

+        for (int i = 0; i < d.length; i++) {

+            s += (((i == 0) ? "[ " : " , ") + trim(d[i]));

+        }

+        Log.v(LOGTAG, s + "]");

+    }

+

+    public static void main(String[] args) {

+        int[] voldim = {50, 50, 100};

+        double[] mEyePoint = {voldim[0] / 2., -voldim[1] / 2., voldim[2] / 2.};

+        double[] mLookPoint = {voldim[0] / 2., voldim[1] / 2., voldim[2] / 2.};

+        double[] mUpVector = {0., 0., 1.};

+

+        Transform t = new Transform();

+        t.mVoxelDim[0] = 1;

+        t.setEyePoint(mEyePoint);

+        t.setLookPoint(mLookPoint);

+        t.setUpVector(mUpVector);

+        t.setScreenDim(256, 256);

+        t.setScreenWidth(128);

+        t.updateAllMatrix();

+

+        Matrix m = t.getMatrix(SCREEN_SPACE, VOLUME_SPACE);

+        float[] orig = {.5f, .5f, 0};

+        float[] ret = new float[3];

+

+        m.mult3(orig, ret);

+        print(ret);

+        float[] look = {0, 0, 1};

+        m.mult3v(look, ret);

+        print(ret);

+        float[] up = {1, 0, 0};

+        m.mult3v(up, ret);

+        print(ret);

+        float[] right = {0, 1, 0};

+        m.mult3v(right, ret);

+        print(ret);

+

+    }

+

+    public void print() {

+        Log.v(LOGTAG, "==== =========== VIEW ========== ======");

+

+        mViewMatrix.print();

+        Log.v(LOGTAG, "==== SCREEN_SPACE to WORLD_SPACE ======");

+        mAllMat[SCREEN_SPACE][WORLD_SPACE].print();

+        Log.v(LOGTAG, "==== SCREEN_SPACE to VOLUME_SPACE ======");

+        mAllMat[SCREEN_SPACE][VOLUME_SPACE].print();

+        Log.v(LOGTAG, "==== WORLD_SPACE to VOLUME_SPACE ======");

+        mAllMat[WORLD_SPACE][VOLUME_SPACE].print();

+        Log.v(LOGTAG, "==== WORLD_SPACE to SCREEN_SPACE ======");

+        mAllMat[WORLD_SPACE][SCREEN_SPACE].print();

+        Log.v(LOGTAG, "==== VOLUME_SPACE to SCREEN_SPACE ======");

+        mAllMat[VOLUME_SPACE][SCREEN_SPACE].print();

+        Log.v(LOGTAG, "==== VOLUME_SPACE to WORLD_SPACE ======");

+        mAllMat[VOLUME_SPACE][WORLD_SPACE].print();

+        Log.v(LOGTAG, "=======================================");

+    }

+}
\ No newline at end of file
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/TriData.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/TriData.java
new file mode 100644
index 0000000..316475a
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/TriData.java
@@ -0,0 +1,125 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.example.android.rs.vr.engine;

+

+import android.util.Log;

+

+import java.io.FileReader;

+import java.io.LineNumberReader;

+import java.text.DecimalFormat;

+import java.util.Arrays;

+

+/**

+ * Simple representation triangulated surface

+ */

+public class TriData {

+    private static final String LOGTAG = "TriData";

+    protected float[] mVert;

+    protected int[] mIndex;

+

+    public TriData() {

+     }

+

+    public void print() {

+        class Fmt extends DecimalFormat {

+            public Fmt() {

+                super("      ##0.000");

+            }

+

+            public String f(double number) {

+                String ret = "           "+super.format(number);

+                return ret.substring(ret.length() - 7);

+            }

+        }

+        Fmt df = new Fmt();

+        for (int i = 0; i < mVert.length; i += 3) {

+

+            String s = (i / 3 + "[ " + df.f(mVert[i]));

+            s += (", " + df.f(mVert[i + 1]));

+            Log.v(LOGTAG, s + ", " + df.f(mVert[i + 2]) + "]");

+        }

+    }

+

+    public TriData(TriData clone) {

+

+        mVert = Arrays.copyOf(clone.mVert, clone.mVert.length);

+        mIndex = Arrays.copyOf(clone.mIndex, clone.mIndex.length);

+    }

+

+    public void scale(float[] s) {

+        for (int i = 0; i < mVert.length; i += 3) {

+            mVert[i] *= s[0];

+            mVert[i + 1] *= s[1];

+            mVert[i + 2] *= s[2];

+        }

+    }

+

+    public void scale(double[] s) {

+        for (int i = 0; i < mVert.length; i += 3) {

+            mVert[i] *= s[0];

+            mVert[i + 1] *= s[1];

+            mVert[i + 2] *= s[2];

+        }

+    }

+

+    public void transform(Matrix m) {

+        for (int i = 0; i < mVert.length; i += 3) {

+            m.mult3(mVert, i, mVert, i);

+        }

+    }

+

+    public void transform(Matrix m, TriData out) {

+

+        for (int i = 0; i < mVert.length; i += 3) {

+            m.mult3(mVert, i, out.mVert, i);

+        }

+    }

+

+    /**

+     * Read some simple triangle format used in testing

+     * @param fileName

+     */

+    public void read(String fileName) {

+        try {

+            FileReader fr = new FileReader(fileName);

+            LineNumberReader lnr = new LineNumberReader(fr);

+            int num_verts = Integer.parseInt(lnr.readLine());

+            Log.v(LOGTAG, "verts =" + num_verts);

+            mVert = new float[num_verts * 3];

+            int k = 0;

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

+                String[] s = lnr.readLine().split("\\s");

+

+                for (int j = 0; j < s.length; j++) {

+                    mVert[k++] = Float.parseFloat(s[j]);

+                }

+            }

+            int num_tri = Integer.parseInt(lnr.readLine());

+            Log.v(LOGTAG, "tri =" + num_tri);

+            mIndex = new int[3 * num_tri];

+            k = 0;

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

+                String[] s = lnr.readLine().split("\\s");

+                for (int j = 0; j < s.length; j++) {

+                    mIndex[k++] = Integer.parseInt(s[j]);

+                }

+            }

+        } catch (Exception e) {

+            e.printStackTrace();

+        }

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VectorUtil.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VectorUtil.java
new file mode 100644
index 0000000..973d705
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VectorUtil.java
@@ -0,0 +1,66 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.example.android.rs.vr.engine;

+

+/**

+ * Some minimal utilities for Vector math

+ */

+public class VectorUtil {

+    public static void sub(double[] a, double[] b, double[] out) {

+        out[0] = a[0] - b[0];

+        out[1] = a[1] - b[1];

+        out[2] = a[2] - b[2];

+    }

+

+    public static void mult(double[] a, double b, double[] out) {

+        out[0] = a[0] * b;

+        out[1] = a[1] * b;

+        out[2] = a[2] * b;

+    }

+

+    public static double dot(double[] a, double[] b) {

+        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];

+    }

+

+    public static double norm(double[] a) {

+        return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);

+    }

+

+    public static void cross(double[] a, double[] b, double[] out) {

+        double out0 = a[1] * b[2] - b[1] * a[2];

+        double out1 = a[2] * b[0] - b[2] * a[0];

+        double out2 = a[0] * b[1] - b[0] * a[1];

+        out[0] = out0;

+        out[1] = out1;

+        out[2] = out2;

+    }

+

+    public static void normalize(double[] a) {

+        double norm = norm(a);

+        a[0] /= norm;

+        a[1] /= norm;

+        a[2] /= norm;

+    }

+

+    public static void add(double[] a, double[] b,

+                           double[] out) {

+        out[0] = a[0] + b[0];

+        out[1] = a[1] + b[1];

+        out[2] = a[2] + b[2];

+    }

+

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/ViewMatrix.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/ViewMatrix.java
new file mode 100644
index 0000000..b7c172a
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/ViewMatrix.java
@@ -0,0 +1,369 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.example.android.rs.vr.engine;

+

+import android.util.Log;

+

+import java.text.DecimalFormat;

+import java.util.Arrays;

+

+public class ViewMatrix extends Matrix {

+    private static final String LOGTAG = "ViewMatrix";

+    private double[] mLookPoint;

+    private double[] mEyePoint;

+    private double[] mUpVector;

+    private double mScreenWidth;

+    private int[] mScreenDim;

+

+    private Matrix mStartMatrix;

+    private double[] mStartV = new double[3];

+    private double[] mMoveToV = new double[3];

+    private double[] mStartEyePoint;

+    private double[] mStartUpVector;

+    private Quaternion mQ = new Quaternion(0, 0, 0, 0);

+

+    public final static char UP_AT = 0x001;

+    public final static char DOWN_AT = 0x002;

+    public final static char RIGHT_AT = 0x010;

+    public final static char LEFT_AT = 0x020;

+    public final static char FORWARD_AT = 0x100;

+    public final static char BEHIND_AT = 0x200;

+

+    public void clone(ViewMatrix src) {

+        if (src.mLookPoint != null) {

+            System.arraycopy(src.mLookPoint, 0, mLookPoint, 0, mLookPoint.length);

+        }

+        if (src.mEyePoint != null) {

+            System.arraycopy(src.mEyePoint, 0, mEyePoint, 0, mEyePoint.length);

+        }

+        if (src.mUpVector != null) {

+            System.arraycopy(src.mUpVector, 0, mUpVector, 0, mUpVector.length);

+        }

+        mScreenWidth = src.mScreenWidth;

+

+        if (src.mScreenDim != null) {

+            System.arraycopy(src.mScreenDim, 0, mScreenDim, 0, mScreenDim.length);

+        }

+        if (src.mStartV != null) {

+            System.arraycopy(src.mStartV, 0, mStartV, 0, mStartV.length);

+        }

+        if (src.mMoveToV != null) {

+            System.arraycopy(src.mMoveToV, 0, mMoveToV, 0, mMoveToV.length);

+        }

+

+

+        if (src.mStartEyePoint != null) {

+            if (mStartEyePoint == null) {

+                mStartEyePoint = Arrays.copyOf(src.mStartEyePoint,src.mStartEyePoint.length);

+            } else {

+                System.arraycopy(src.mStartEyePoint, 0, mStartEyePoint, 0, mStartEyePoint.length);

+            }

+        }

+        if (src.mStartUpVector != null) {

+            if (mStartUpVector == null) {

+                mStartUpVector = Arrays.copyOf(src.mStartUpVector,src.mStartUpVector.length);

+            } else {

+                System.arraycopy(src.mStartUpVector, 0, mStartUpVector, 0, mStartUpVector.length);

+            }

+        }

+        if (src.mStartMatrix != null) {

+            if (mStartMatrix == null) {

+                mStartMatrix = new Matrix();

+            }

+            mStartMatrix.clone(src.mStartMatrix);

+        }

+        mQ.clone(src.mQ);

+        super.clone(src);

+    }

+

+    private static String toStr(double d) {

+        String s = "         " + df.format(d);

+        return s.substring(s.length() - 9);

+    }

+

+    private static String toStr(double[] d) {

+        String s = "[";

+        for (int i = 0; i < d.length; i++) {

+            s += toStr(d[i]);

+        }

+

+        return s + "]";

+    }

+

+    private static DecimalFormat df = new DecimalFormat("##0.000");

+

+    @Override

+    public void print() {

+        Log.v(LOGTAG, "mLookPoint  :" + toStr(mLookPoint));

+        Log.v(LOGTAG, "mEyePoint   :" + toStr(mEyePoint));

+        Log.v(LOGTAG, "mUpVector   :" + toStr(mUpVector));

+        Log.v(LOGTAG, "mScreenWidth: " + toStr(mScreenWidth));

+        Log.v(LOGTAG, "mScreenDim  :[" + mScreenDim[0] + ", " + mScreenDim[1] + "]");

+    }

+

+    public ViewMatrix() {

+        mLookPoint = new double[3];

+        mEyePoint = new double[3];

+        mUpVector = new double[3];

+        mScreenDim = new int[2];

+    }

+

+    public void setScreenDim(int x, int y) {

+        mScreenDim = new int[]{x, y};

+    }

+

+    public double[] getLookPoint() {

+        return mLookPoint;

+    }

+

+    public void setLookPoint(double[] mLookPoint) {

+        this.mLookPoint = mLookPoint;

+    }

+

+    public double[] getEyePoint() {

+        return mEyePoint;

+    }

+

+    public void setEyePoint(double[] mEyePoint) {

+        this.mEyePoint = mEyePoint;

+    }

+

+    public double[] getUpVector() {

+        return mUpVector;

+    }

+

+    public void setUpVector(double[] mUpVector) {

+        this.mUpVector = mUpVector;

+    }

+

+    public double getScreenWidth() {

+        return mScreenWidth;

+    }

+

+    public void setScreenWidth(double screenWidth) {

+        this.mScreenWidth = screenWidth;

+    }

+

+    public void makeUnit() {

+

+    }

+

+    public void calcMatrix() {

+        if (mScreenDim == null) {

+            return;

+        }

+        double scale = mScreenWidth / mScreenDim[0];

+        double[] zv = {

+                mLookPoint[0] - mEyePoint[0],

+                mLookPoint[1] - mEyePoint[1],

+                mLookPoint[2] - mEyePoint[2]

+        };

+        VectorUtil.normalize(zv);

+

+

+        double[] m = new double[16];

+        m[2] = zv[0];

+        m[6] = zv[1];

+        m[10] = zv[2];

+        m[14] = 0;

+

+        calcRight(zv, mUpVector, zv);

+        double[] right = zv;

+

+        m[0] = right[0] * scale;

+        m[4] = right[1] * scale;

+        m[8] = right[2] * scale;

+        m[12] = 0;

+

+        m[1] = -mUpVector[0] * scale;

+        m[5] = -mUpVector[1] * scale;

+        m[9] = -mUpVector[2] * scale;

+        m[13] = 0;

+        double sw = mScreenDim[0] / 2 - 0.5;

+        double sh = mScreenDim[1] / 2 - 0.5;

+        double sz = -0.5;

+        m[3] = mEyePoint[0] - (m[0] * sw + m[1] * sh + m[2] * sz);

+        m[7] = mEyePoint[1] - (m[4] * sw + m[5] * sh + m[6] * sz);

+        m[11] = mEyePoint[2] - (m[8] * sw + m[9] * sh + m[10] * sz);

+

+        m[15] = 1;

+        this.m = m;

+    }

+

+    static void calcRight(double[] a, double[] b, double[] out) {

+        VectorUtil.cross(a, b, out);

+    }

+

+    public static void main(String[] args) {

+        double[] up = {0, 0, 1};

+        double[] look = {0, 0, 0};

+        double[] eye = {-10, 0, 0};

+        ViewMatrix v = new ViewMatrix();

+        v.setEyePoint(eye);

+        v.setLookPoint(look);

+        v.setUpVector(up);

+        v.setScreenWidth(10);

+        v.setScreenDim(512, 512);

+        v.calcMatrix();

+    }

+

+    private void calcLook(TriData tri, float[] voxelDim, int w, int h) {

+        float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;

+        float maxx = -Float.MAX_VALUE, maxy = -Float.MAX_VALUE, maxz = -Float.MAX_VALUE;

+

+        for (int i = 0; i < tri.mVert.length; i += 3) {

+            maxx = Math.max(tri.mVert[i], maxx);

+            minx = Math.min(tri.mVert[i], minx);

+            maxy = Math.max(tri.mVert[i + 1], maxy);

+            miny = Math.min(tri.mVert[i + 1], miny);

+            maxz = Math.max(tri.mVert[i + 2], maxz);

+            minz = Math.min(tri.mVert[i + 2], minz);

+        }

+        mLookPoint = new double[]{voxelDim[0] * (maxx + minx) / 2,

+                voxelDim[1] * (maxy + miny) / 2,

+                voxelDim[2] * (maxz + minz) / 2};

+        mScreenWidth = Math.max(voxelDim[0] * (maxx - minx),

+                Math.max(voxelDim[1] * (maxy - miny),

+                        voxelDim[2] * (maxz - minz))) * 2;

+    }

+

+    private void calcLook(TriData triW, int w, int h) {

+        float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;

+        float maxx = -Float.MAX_VALUE, maxy = -Float.MAX_VALUE, maxz = -Float.MAX_VALUE;

+

+        for (int i = 0; i < triW.mVert.length; i += 3) {

+            maxx = Math.max(triW.mVert[i], maxx);

+            minx = Math.min(triW.mVert[i], minx);

+            maxy = Math.max(triW.mVert[i + 1], maxy);

+            miny = Math.min(triW.mVert[i + 1], miny);

+            maxz = Math.max(triW.mVert[i + 2], maxz);

+            minz = Math.min(triW.mVert[i + 2], minz);

+        }

+        mLookPoint = new double[]{(maxx + minx) / 2, (maxy + miny) / 2, (maxz + minz) / 2};

+

+        mScreenWidth = 2 * Math.max((maxx - minx), Math.max((maxy - miny), (maxz - minz)));

+    }

+

+    public void look(char dir, TriData tri, float[] voxelDim, int w, int h) {

+        calcLook(tri, w, h);

+        int dx = ((dir >> 4) & 0xF);

+        int dy = ((dir >> 8) & 0xF);

+        int dz = ((dir >> 0) & 0xF);

+        if (dx > 1) {

+            dx = -1;

+        }

+        if (dy > 1) {

+            dy = -1;

+        }

+        if (dz > 1) {

+            dz = -1;

+        }

+        mEyePoint = new double[]{mLookPoint[0] + 2 * mScreenWidth * dx,

+                mLookPoint[1] + 2 * mScreenWidth * dy,

+                mLookPoint[2] + 2 * mScreenWidth * dz};

+        double[] zv = new double[]{-dx, -dy, -dz};

+        double[] rv = new double[]{(dx == 0) ? 1 : 0, (dx == 0) ? 0 : 1, 0};

+        double[] up = new double[3];

+        VectorUtil.norm(zv);

+        VectorUtil.norm(rv);

+

+        VectorUtil.cross(zv, rv, up);

+        VectorUtil.cross(zv, up, rv);

+        VectorUtil.cross(zv, rv, up);

+        mUpVector = up;

+        mScreenDim = new int[]{w, h};

+        calcMatrix();

+    }

+

+    public void lookAt(TriData tri, float[] voxelDim, int w, int h) {

+        calcLook(tri, voxelDim, w, h);

+

+        mEyePoint = new double[]{mLookPoint[0] + mScreenWidth,

+                mLookPoint[1] + mScreenWidth,

+                mLookPoint[2] + mScreenWidth};

+        double[] zv = new double[]{-1, -1, -1};

+        double[] rv = new double[]{1, 1, 0};

+        double[] up = new double[3];

+        VectorUtil.norm(zv);

+        VectorUtil.norm(rv);

+

+        VectorUtil.cross(zv, rv, up);

+        VectorUtil.cross(zv, up, rv);

+        VectorUtil.cross(zv, rv, up);

+        mUpVector = up;

+        mScreenDim = new int[]{w, h};

+        calcMatrix();

+    }

+

+    public void trackBallUP(float x, float y) {

+

+    }

+

+    public void trackBallDown(float x, float y) {

+

+        ballToVec(x, y, mStartV);

+        mStartEyePoint = Arrays.copyOf(mEyePoint, m.length);

+        mStartUpVector = Arrays.copyOf(mUpVector, m.length);

+        mStartMatrix = new Matrix(this);

+        mStartMatrix.makeRotation();

+    }

+

+    public void trackBallMove(float x, float y) {

+        ballToVec(x, y, mMoveToV);

+

+        double angle = Quaternion.calcAngle(mStartV, mMoveToV);

+        if (angle < 0.0001) {

+            calcMatrix();

+            return;

+        }

+        double[] axis = Quaternion.calcAxis(mStartV, mMoveToV);

+

+

+        axis = mStartMatrix.vecmult(axis);

+

+        mQ.set(angle, axis);

+

+        VectorUtil.sub(mLookPoint, mStartEyePoint, mEyePoint);

+

+        mEyePoint = mQ.rotateVec(mEyePoint);

+        mUpVector = mQ.rotateVec(mStartUpVector);

+

+        VectorUtil.sub(mLookPoint, mEyePoint, mEyePoint);

+        calcMatrix();

+    }

+

+    void ballToVec(float x, float y, double[] v) {

+        float ballRadius = Math.min(mScreenDim[0], mScreenDim[1]) * .4f;

+        double cx = mScreenDim[0] / 2.;

+        double cy = mScreenDim[1] / 2.;

+

+        double dx = (cx - x) / ballRadius;

+        double dy = (cy - y) / ballRadius;

+        double scale = dx * dx + dy * dy;

+        if (scale > 1) {

+            scale = Math.sqrt(scale);

+            dx = dx / scale;

+            dy = dy / scale;

+        }

+

+        double dz = Math.sqrt(Math.abs(1 - (dx * dx + dy * dy)));

+        v[0] = dx;

+        v[1] = dy;

+        v[2] = dz;

+        VectorUtil.normalize(v);

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Volume.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Volume.java
new file mode 100644
index 0000000..0de5544
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/Volume.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.example.android.rs.vr.engine;

+

+import android.renderscript.Allocation;

+import android.util.Log;

+

+import java.util.Arrays;

+import java.util.HashMap;

+

+/**

+ * Defines a simple volume to be used in the volume renderer

+ */

+public class Volume {

+    private static final String LOGTAG = "Volume";

+    public short[][] mData;

+    public Allocation mVolumeAllocation; // one big volume

+    public int mDimz = -1;

+    public int mDimy = -1;

+    public int mDimx = -1;

+    public float[] mVoxelDim = new float[]{1f, 1f, 1f};

+    private HashMap<String, Look> mLooks = new HashMap<String, Look>();

+

+    @Override

+    public String toString() {

+        String ret = "Volume[" + mDimx + "," + mDimy + "," + mDimz + "]";

+        ret += "(" + mVoxelDim[0] + ", " + mVoxelDim[1] + ", " + mVoxelDim[2] + ")";

+

+        return ret;

+    }

+

+    public String[] getLookNames() {

+        return mLooks.keySet().toArray(new String[mLooks.size()]);

+    }

+

+    public int[][] getLookColor(String name) {

+        return mLooks.get(name).mColor;

+    }

+

+    public int[][] getLookOpactiy(String name) {

+        return mLooks.get(name).mOpacity;

+    }

+

+    public void addLook(String name, int[][] color,  int[][] opacity) {

+        mLooks.put(name, new Look(name, color, opacity));

+    }

+

+    public void addLook(String name, String color_string, String opacity_string) {

+        mLooks.put(name, new Look(name, color_string, opacity_string));

+        Look l = mLooks.get(name);

+        Log.v(LOGTAG, " ========================== " + name + " =============================");

+        Log.v(LOGTAG, "mColor "+l.dblArrayToString(l.mColor));

+        Log.v(LOGTAG, "mOpacity "+l.dblArrayToString(l.mOpacity));

+    }

+

+    class Look {

+        int[][] mColor;

+        int[][] mOpacity;

+        String mName;

+

+        public Look(String name, String color_string, String opacity_string) {

+            mName = name;

+            String[] colorSplit = color_string.split("\\}\\s*\\,\\s*\\{");

+            String[] opacitySplit = opacity_string.split("\\}\\s*\\,\\s*\\{");

+            mColor = new int[colorSplit.length][];

+            for (int i = 0; i < colorSplit.length; i++) {

+

+                mColor[i] = readNumbers(colorSplit[i]);

+            }

+            mOpacity = new int[opacitySplit.length][];

+            for (int i = 0; i < opacitySplit.length; i++) {

+

+                mOpacity[i] = readNumbers(opacitySplit[i]);

+            }

+        }

+

+        public Look(String name, int[][] color, int[][] opacity) {

+            mColor = color;

+            mOpacity = opacity;

+            mName =name;

+        }

+

+        private int[] readNumbers(String numList) {

+            numList = numList.replace('{', ' ');

+            numList = numList.replace('}', ' ');

+            numList = numList.replace(';', ' ');

+            String[] split = numList.split(",");

+            int[] ret = new int[split.length];

+            for (int i = 0; i < ret.length; i++) {

+                ret[i] = Integer.decode(split[i].trim());

+            }

+            return ret;

+        }

+

+        private String dblArrayToString(int[][] v) {

+            String s = "";

+            for (int i = 0; i < v.length; i++) {

+                if (i > 0) {

+                    s += ",";

+                }

+                s += Arrays.toString(v[i]);

+            }

+            return s;

+        }

+

+        public String toString() {

+            return "mColor=" + dblArrayToString(mColor) + "\nmOpacity=" + dblArrayToString(mOpacity);

+        }

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrPipline1.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrPipline1.java
new file mode 100644
index 0000000..291a588
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrPipline1.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.engine;
+
+import android.renderscript.Matrix3f;
+import android.renderscript.Matrix4f;
+import android.renderscript.Script;
+import android.renderscript.ScriptIntrinsicResize;
+import android.util.Log;
+
+import java.text.DecimalFormat;
+
+public class VrPipline1 extends BasicPipeline {
+    private static final String LOGTAG = "VrPipline1";
+
+    float[] mMatrixBuffer = new float[16];
+    ScriptC_vr scriptC_vr;
+    ScriptIntrinsicResize script_resize;
+    Script.LaunchOptions options = new Script.LaunchOptions();
+
+    @Override
+    public void initBuffers(VrState state) {
+        super.initBuffers(state);
+    }
+
+    static DecimalFormat df = new DecimalFormat("      ##0.000");
+
+    private static String trim(double d) {
+        String s = df.format(d);
+        return s.substring(s.length() - 6);
+    }
+
+    private static String trim(float[] d) {
+        String ret = "";
+        for (int i = 0; i < d.length; i++) {
+            ret += ((i == 0) ? "[ " : " , ") + trim(d[i]);
+        }
+        return ret + ("]");
+    }
+
+    private void creatOpacityAllocation(VrState state) {
+        scriptC_vr.set_opacity(state.mMaterial.getOpacityAllocation(state.mRs));
+    }
+
+    private void creatColorMapAllocation(VrState state) {
+        scriptC_vr.set_color_map(state.mMaterial.getColorMapAllocation(state.mRs));
+    }
+
+    @Override
+    public void setupTriangles(VrState state) {
+        super.setupTriangles(state);
+        if (mCancel){
+            return;
+        }
+        Matrix m = state.mTransform.getMatrix(Transform.SCREEN_SPACE, Transform.VOLUME_SPACE);
+        m.getAsFloats(mMatrixBuffer);
+        Matrix4f matrix4f = new Matrix4f(mMatrixBuffer);
+        if (scriptC_vr == null) {
+            scriptC_vr = new ScriptC_vr(state.mRs);
+        }
+        if (script_resize == null) {
+            script_resize = ScriptIntrinsicResize.create(state.mRs);
+        }
+        scriptC_vr.set_matrix4(matrix4f);
+        for (int i = 0; i < 9; i++) {
+            int x = i % 3;
+            int y = i / 3;
+            mMatrixBuffer[i] = mMatrixBuffer[x + y * 4];
+        }
+        Matrix3f matrix3f = new Matrix3f(mMatrixBuffer);
+        scriptC_vr.set_matrix3(matrix3f);
+        creatColorMapAllocation(state);
+        creatOpacityAllocation(state);
+        scriptC_vr.invoke_setup_vectors();
+    }
+
+    @Override
+    public void raycast(VrState state) {
+        if (mCancel){
+            return;
+        }
+        scriptC_vr.set_volume(state.mVolume.mVolumeAllocation);
+        scriptC_vr.set_bricks(state.mRsMask.mBrick_allocation);
+        scriptC_vr.set_brick_dimx(state.mRsMask.m_bricks_dimx);
+        scriptC_vr.set_brick_dimy(state.mRsMask.m_bricks_dimy);
+        if (mCancel){
+            return;
+        }
+        scriptC_vr.set_zbuff(state.mzRangeFullAllocation);
+        if (mCancel){
+            return;
+        }
+        if (state.mImgWidth*state.mImgHeight < 512*512) {
+            scriptC_vr.forEach_draw_z_buffer(state.mzRangeFullAllocation, state.mScrAllocation);
+        } else {
+            int blocks = state.mImgWidth*state.mImgHeight/(256*256);
+            for (int i = 0; i < blocks; i++) {
+                options.setX(0,state.mImgWidth);
+                options.setY(i*state.mImgHeight/blocks, (i+1)*state.mImgHeight/blocks);
+                scriptC_vr.forEach_draw_z_buffer(state.mzRangeFullAllocation, state.mScrAllocation, options);
+                state.mRs.finish();
+                if (mCancel){
+                    Log.v(LOGTAG, "cancel");
+                    return;
+                }
+            }
+
+
+        }
+
+    }
+
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrState.java b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrState.java
new file mode 100644
index 0000000..1ac5738
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/VrState.java
@@ -0,0 +1,126 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.example.android.rs.vr.engine;

+

+import android.graphics.SurfaceTexture;

+import android.renderscript.Allocation;

+import android.renderscript.Element;

+import android.renderscript.RenderScript;

+import android.renderscript.Type;

+import android.util.Log;

+import android.view.Surface;

+

+public class VrState {

+    private static final String LOGTAG = "VrState";

+    public RenderScript mRs;

+    public Volume mVolume;

+    public RsBrickedBitMask mRsMask;

+    public Material mMaterial = new Material();

+    public Cube mCubeVolume;

+    public TriData mCubeScreen;

+    public int mImgWidth;

+    public int mImgHeight;

+    Allocation mzRangeFullAllocation;

+    public Allocation mScrAllocation; // the RGB data out

+    public Transform mTransform = new Transform();

+

+    public void clone(VrState src) {

+        mRs = src.mRs;

+        mVolume = src.mVolume;

+        mRsMask = src.mRsMask;

+        mMaterial = src.mMaterial;

+        if (mCubeVolume == null) {

+            mCubeVolume = new Cube();

+        }

+        mCubeVolume.clone(src.mCubeVolume);

+        mCubeScreen = new TriData(src.mCubeScreen);

+        mImgWidth = src.mImgWidth;

+        mImgHeight = src.mImgHeight;

+        mzRangeFullAllocation = src.mzRangeFullAllocation;

+        mScrAllocation = src.mScrAllocation;

+        mTransform.clone(src.mTransform);

+    }

+

+    public void createOutputAllocation(Surface surface, int w, int h) {

+

+        if (mRs == null) {

+            return;

+        }

+

+        if (mScrAllocation == null

+                || mScrAllocation.getType().getX() != w

+                || mScrAllocation.getType().getY() != h) {

+            if (mScrAllocation != null) {

+                mScrAllocation.destroy();

+                mScrAllocation = null;

+                Log.v(LOGTAG, " destroy mScrAllocation");

+            }

+            Type.Builder b = new Type.Builder(mRs, Element.RGBA_8888(mRs));

+            b.setX(w);

+            b.setY(h);

+

+            mScrAllocation = Allocation.createTyped(mRs, b.create(),

+                    Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);

+

+        }

+        mScrAllocation.setSurface(surface);

+

+        if (mzRangeFullAllocation == null

+                || mzRangeFullAllocation.getType().getX() != w

+                || mzRangeFullAllocation.getType().getY() != h) {

+            if (mzRangeFullAllocation != null) {

+                mzRangeFullAllocation.destroy();

+                mzRangeFullAllocation = null;

+                Log.v(LOGTAG, " destroy mzRangeFullAllocation");

+

+            }

+            Type.Builder b = new Type.Builder(mRs, Element.F32_2(mRs));

+            b.setX(w);

+            b.setY(h);

+

+            mzRangeFullAllocation = Allocation.createTyped(mRs, b.create());

+        }

+

+        mImgWidth = w;

+        mImgHeight = h;

+        mTransform.setScreenDim(w, h);

+    }

+

+    public void copyData(VrState src) {

+        mRs = src.mRs;

+        mVolume = src.mVolume;

+        mRsMask = src.mRsMask;

+        mMaterial = src.mMaterial;

+        if (mCubeVolume == null) {

+            mCubeVolume = new Cube();

+        }

+        mCubeVolume.clone(src.mCubeVolume);

+        mCubeScreen = new TriData(src.mCubeScreen); // TODO I should not have to do new each time

+        mImgWidth = src.mImgWidth;

+        mImgHeight = src.mImgHeight;

+        mTransform.clone(src.mTransform);

+

+    }

+

+    public void destroyScreenAllocation() {

+        Log.v(LOGTAG, "destroyScreenAllocation");

+        if (mScrAllocation != null) {

+            mScrAllocation.destroy();

+            mScrAllocation = null;

+        }

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/bricked.rs b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/bricked.rs
new file mode 100644
index 0000000..808722c
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/bricked.rs
@@ -0,0 +1,206 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+#pragma version(1)

+#pragma rs java_package_name(com.example.android.rs.vr.engine)

+#pragma rs_fp_relaxed

+

+int brick_dimx;

+int brick_dimy;

+int brick_dimz;

+rs_allocation volume;

+rs_allocation opacity;

+int z_offset;

+// output a single bit per pixel volume based on opacity

+

+uint __attribute__((kernel)) pack_chunk(uint32_t x) {

+

+    int brick = x / (32 * 32);

+    int bx = brick % brick_dimx;

+    int yz = brick / brick_dimx;

+    int by = yz % brick_dimy;

+    int bz = yz / brick_dimy;

+

+    int in_brick = x % (32 * 32);

+    int in_br_y = in_brick % 32;

+    int in_br_z = in_brick / 32;

+    int pz = (bz << 5) | in_br_z;

+

+

+     int py = (by << 5) | in_br_y;

+

+    uint out = 0;

+

+    if (pz >= rsAllocationGetDimZ(volume)) {

+         return out;

+    }

+    if (py >= rsAllocationGetDimY(volume)) {

+              return out;

+    }

+    for (int in_br_x = 0; in_br_x < 32; in_br_x++) {

+        int px = (bx << 5) | in_br_x;

+

+

+        int intensity = 0xFFFF & rsGetElementAt_short(volume, px, py, pz);

+        uchar op = rsGetElementAt_uchar(opacity, intensity);

+        uint v = (op > 0) ? 1 : 0;

+        out |= v << in_br_x;

+    }

+    return out;

+}

+rs_allocation bricks; // input bricks

+

+uint __attribute__((kernel)) dilate(uint in, uint32_t x) {

+

+    int BRICK_SIZE = 32 * 32;

+    int brick = x / (BRICK_SIZE);

+    int bx = brick % brick_dimx;

+    int yz = brick / brick_dimx;

+    int by = yz % brick_dimy;

+    int bz = yz / brick_dimy;

+

+    int in_brick = x % (BRICK_SIZE);

+    int in_br_y = in_brick % 32;

+    int in_br_z = in_brick / 32;

+    uint slice;

+    uint out = in;

+    out |= in >> 1;

+    out |= in << 1;

+    int base_brick = bx + (by + brick_dimy * bz) * brick_dimx;

+    int base_offset = (in_br_z) * 32 + (in_br_y);

+

+      int slice_pos = base_brick * BRICK_SIZE + base_offset;

+

+      // +/- in x

+      if (bx > 0) {

+        slice =   0x80000000 & rsGetElementAt_uint(bricks, slice_pos - BRICK_SIZE);

+        out |= slice >> 31;

+      }

+      if (bx < brick_dimx - 1) {

+        slice =  1  & rsGetElementAt_uint(bricks, slice_pos + BRICK_SIZE);

+         out |= slice << 31;

+      }

+

+      // - in Y

+      int off_neg_y = -1; // simple case -1 slice;

+      if (in_br_y == 0) { // att the edge in brick go to y-1 brick

+        if (by > 0) { // edge of screen

+          off_neg_y = 31 - BRICK_SIZE * brick_dimx;

+        } else {// edge of volume

+          off_neg_y = 0;

+        }

+      }

+

+      slice = rsGetElementAt_uint(bricks, slice_pos + off_neg_y);

+      out |= slice;

+      // + in Y

+      int off_pos_y = 1;

+      if (in_br_y == 31) {

+        if (by < brick_dimy - 1) {

+          off_pos_y = BRICK_SIZE * brick_dimx - 31;

+        } else {

+          off_pos_y = 0;

+        }

+      }

+      slice = rsGetElementAt_uint(bricks, slice_pos + off_pos_y);

+      out |= slice;

+      int off_neg_z = -32;

+      if (in_br_z == 0) {

+        if (bz > 0) { // edge of screen

+          off_neg_z = 31*32 - brick_dimx * brick_dimy* BRICK_SIZE;

+        } else {

+          off_neg_z = 0;

+        }

+      }

+      slice = rsGetElementAt_uint(bricks, slice_pos + off_neg_z);

+      out |= slice;

+      int off_pos_z = 32;

+      if (in_br_z == 31) {

+        if (bz < brick_dimz - 1) {

+          off_pos_z = brick_dimx * brick_dimy * BRICK_SIZE - 31*32;

+        } else {

+          off_pos_z = 0;

+        }

+      }

+      slice = rsGetElementAt_uint(bricks, slice_pos + off_pos_z);

+      out |= slice;

+

+    return out;

+

+

+

+}

+int z;

+

+void __attribute__((kernel)) copy(short in, uint32_t x, uint32_t y) {

+    rsSetElementAt_short(volume, in, x, y, z);

+}

+

+int size;

+

+static float3 nylander(float3 p, int n) {

+    float3 out;

+    float r = length(p);

+    float phi = atan2(p.y, p.x);

+    float theta = acos(p.z / r);

+    float rn = pow(r, n);

+    out.x = sin(n * theta) * cos(n * phi);

+    out.y = sin(n * theta) * sin(n * phi);

+    out.z = cos(n * theta);

+    return out * rn;

+}

+

+/**

+* 8 x faster than the above for n = 3

+*/

+static float3 nylander3(float3 p) {

+    float3 out = (float3){0.f, 0.f, 0.f};

+    float xy2 = p.x * p.x + p.y * p.y;

+    if (xy2 == 0) return out;

+    float z23x2y2 = (3 * p.z * p.z - p.x * p.x - p.y * p.y);

+    out.x = (z23x2y2 * p.x * (p.x * p.x - 3 * p.y * p.y)) / xy2;

+    out.y = (z23x2y2 * p.y * (3 * p.x * p.x - p.y * p.y)) / xy2;

+    out.z = p.z * (p.z * p.z - 3 * p.x * p.x - 3 * p.y * p.y);

+    return out;

+}

+

+static float vsize(float3 p) {

+    return sqrt(p.x * p.x + p.y * p.y + p.z * p.z);

+}

+

+short __attribute__((kernel)) mandelbulb(uint32_t x, uint32_t y) {

+    int size2 = size / 2;

+     if (z < size2) {

+          return 256-4*(size2-z+4)*hypot((float)x-size2,(float)y-size2) / size2 ;

+    }

+    float3 c = (float3) {(float) x, (float) y, (float) z};

+    c = ((c - size2) / (size2 * .9f));

+

+    int loop = 25;

+    float3 p = c;

+    float len;

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

+        //    p = nylander(p, 3) + c;

+        p = nylander3(p) + c;

+        len = fast_length(p);

+        if (len > 2.f) return 255 - loop*10;

+        if (len < .3f) return loop*10;

+

+    }

+    len = length(p);

+    return (short) (255 - (len * 255) / 4);

+}

+

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/rasterize.rs b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/rasterize.rs
new file mode 100644
index 0000000..092a382
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/rasterize.rs
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.vr.engine)
+#pragma rs_fp_relaxed
+
+#define FLOAT_MAX  3.4028234E30f
+
+float vert[3 * 8];
+int index[3 * 12];
+int width;
+int height;
+
+static float zoff[12];
+static float2 slope[12];
+
+static float2 p1[12];
+static float2 p2[12];
+static float2 p3[12];
+
+      float2 d12[12];
+        float2 d23[12];
+        float2 d31[12];
+static int total;
+
+static void
+triangleSetup(float3 f1, float3 f2, float3 f3) {
+    if (((f1.x - f2.x) * (f3.y - f2.y) - (f1.y - f2.y) * (f3.x - f2.x)) < 0) {
+        float3 tmp = f1;
+        f1 = f2;
+        f2 = tmp;
+    }
+
+    // using maxmima
+    // string(solve([x1*dx+y1*dy+zoff=z1,x2*dx+y2*dy+zoff=z2,x3*dx+y3*dy+zoff=z3],[dx,dy,zoff]));
+    double d = (f1.x * (f3.y - f2.y) - f2.x * f3.y + f3.x * f2.y + (f2.x - f3.x)
+            * f1.y);
+
+    if (d == 0) {
+        return;
+    }
+
+    slope[total].x = (-(f1.y * (f3.z - f2.z) - f2.y * f3.z + f3.y * f2.z + (f2.y - f3.y) * f1.z)
+            / d);
+    slope[total].y = ((f1.x * (f3.z - f2.z) - f2.x * f3.z + f3.x * f2.z + (f2.x - f3.x) * f1.z)
+            / d);
+    zoff[total] = ((f1.x * (f3.y * f2.z - f2.y * f3.z) + f1.y * (f2.x * f3.z - f3.x * f2.z) +
+            (f3.x * f2.y - f2.x * f3.y) * f1.z) / d);
+
+    p1[total] = f1.xy;
+    p2[total] = f2.xy;
+    p3[total] = f3.xy;
+    d12[total] = p1[total] - p2[total];
+    d23[total] = p2[total] - p3[total];
+    d31[total] = p3[total] - p1[total];
+    total++;
+}
+
+void setup_triangles(int w, int h) {
+    width = w;
+    height = h;
+    total = 0;
+    //   rsDebug("RRRRR >>>>>> setup_triangles ", w, h);
+    float3 f1;
+    float3 f2;
+    float3 f3;
+    for (int i = 0; i < 3 * 12; i += 3) {
+        int p1 = index[i];
+        int p2 = index[i + 1];
+        int p3 = index[i + 2];
+        f1.x = vert[p1];
+        f1.y = vert[p1 + 1];
+        f1.z = vert[p1 + 2];
+        f2.x = vert[p2];
+        f2.y = vert[p2 + 1];
+        f2.z = vert[p2 + 2];
+        f3.x = vert[p3];
+        f3.y = vert[p3 + 1];
+        f3.z = vert[p3 + 2];
+        triangleSetup(f1, f2, f3);
+    }
+}
+
+
+float2 __attribute__ ((kernel)) render_z(uint32_t x, uint32_t y) {
+    float2 out = (float2) {FLOAT_MAX,-FLOAT_MAX};
+    float2 loc;
+    loc.x = x;
+    loc.y = y;
+    for (int i = 0; i < total; i++) {
+        float2 d1 = loc - p1[i];
+        float2 d2 = loc - p2[i];
+        float2 d3 = loc - p3[i];
+
+        float test1 = (d12[i].x) * (d1.y) - (d12[i].y) * (d1.x);
+        float test2 = (d23[i].x) * (d2.y) - (d23[i].y) * (d2.x);
+        float test3 = (d31[i].x) * (d3.y) - (d31[i].y) * (d3.x);
+        // float test = edge(0 , test1) * edge(0 , test2)* edge(0 , test3);
+
+        if (test1 >= 0 &&
+                test2 >= 0 &&
+                test3 >= 0) {
+            float2 delta = slope[i] * loc;
+            float z = zoff[i] + delta.x + delta.y;
+
+            out.x = min(z, out.x);
+            out.y = max(z, out.y);
+        }
+    }
+    return out;
+}
+
+
+rs_allocation z_range_buff;
+float min_z = 1500;
+float max_z = 2300;
+
+void getMinMax() {
+
+    float tmp_min = FLOAT_MAX;
+    float tmp_max = -FLOAT_MAX;;
+    for (int y = 0; y < height; y++) {
+        for (int x = 0; x < width; x++) {
+            float2 v = rsGetElementAt_float2(z_range_buff, x, y);
+            if (v.x == FLOAT_MAX) {
+                continue;
+            }
+            tmp_min = min(tmp_min, v.x);
+            tmp_max = max(tmp_max, v.x);
+        }
+    }
+    min_z = tmp_min;
+    max_z = tmp_max;
+    rsDebug("RRRRR >>>>>> getMinMax ", min_z, max_z);
+}
+
+uchar4 __attribute__ ((kernel)) draw_z_buffer(float2 in) {
+
+    if (in.x != FLOAT_MAX) {
+        uchar v = (uchar)(255 * (in.x - min_z) / (max_z - min_z));
+        uchar4 out;
+        out.r = v;
+        out.g = v;
+        out.b = v;
+        out.a = 255;
+        return out;
+    }
+
+    uchar4 out;
+    out.r = 0x44;
+    out.g = 0x44;
+    out.b = 0x99;
+    out.a = 255;
+    return out;
+}
\ No newline at end of file
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/engine/vr.rs b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/vr.rs
new file mode 100644
index 0000000..997bb10
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/engine/vr.rs
@@ -0,0 +1,306 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+#pragma version(1)

+#pragma rs java_package_name(com.example.android.rs.vr.engine)

+#pragma rs_fp_relaxed

+

+#define FLOAT_MAX  3.4028234E30f

+#define JITTER_LENGTH 3456

+float jitter[JITTER_LENGTH];

+

+float3 s;

+float3 dx;

+float3 dy;

+float3 dz;

+float zoomFactor;

+rs_matrix4x4 matrix4;

+rs_matrix3x3 matrix3;

+uchar4 base_color;

+static float3 mLight;

+

+// material color

+rs_allocation opacity;

+rs_allocation color_map;

+

+static void fillJitter() {

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

+        jitter[i] = rsRand(1.0f);

+    }

+}

+

+void setup_vectors() {

+    s = rsMatrixMultiply(&matrix4, (float3) {0.5f, 0.5f, 0.5f}).xyz;

+    dx = rsMatrixMultiply(&matrix3, (float3) {1.f, 0.f, 0.f});

+    dy = rsMatrixMultiply(&matrix3, (float3) {0.f, 1.f, 0.f});

+    dz = rsMatrixMultiply(&matrix3, (float3) {0.f, 0.f, 1.f});

+    zoomFactor = dz.x * dz.x + dz.y * dz.y + dz.z * dz.z;

+    zoomFactor /= dx.x * dx.x + dx.y * dx.y + dx.z * dx.z;

+    base_color.r = 0;

+    base_color.g = 0;

+    base_color.b = 0;

+    base_color.a = 255;

+    fillJitter();

+    float3 mLightRelitvePos = (float3) {0, 0.7071f, -0.7071f}; // light relitve to camera

+    mLight = mLightRelitvePos.x + dx + mLightRelitvePos.y * dy + mLightRelitvePos.z * dz;

+    mLight = normalize(mLight);

+}

+

+// old simple version

+static float triLinear_old(short v_0_0_0, short v_0_0_1, short v_0_1_0, short v_0_1_1,

+        short v_1_0_0, short v_1_0_1, short v_1_1_0, short v_1_1_1,

+        float3 delta) {

+    float v_0_0 = v_0_0_0 + delta.x * (v_0_0_1 - v_0_0_0);

+    float v_0_1 = v_0_1_0 + delta.x * (v_0_1_1 - v_0_1_0);

+    float v_1_0 = v_1_0_0 + delta.x * (v_1_0_1 - v_1_0_0);

+    float v_1_1 = v_1_1_0 + delta.x * (v_1_1_1 - v_1_1_0);

+    float v_0 = v_0_0 + delta.y * (v_0_1 - v_0_0);

+    float v_1 = v_1_0 + delta.y * (v_1_1 - v_1_0);

+    float v = v_0 + delta.z * (v_1 - v_0);

+    return v;

+}

+

+//  This seemed to improve over above

+static float triLinear(float v_0_0_0, float v_0_0_1, float v_0_1_0, short v_0_1_1,

+        float v_1_0_0, float v_1_0_1, float v_1_1_0, float v_1_1_1,

+        float3 delta) {

+    float v_0_0 = mix(v_0_0_0, v_0_0_1, delta.x);

+    float v_0_1 = mix(v_0_1_0, v_0_1_1, delta.x);

+    float v_1_0 = mix(v_1_0_0, v_1_0_1, delta.x);

+    float v_1_1 = mix(v_1_1_0, v_1_1_1, delta.x);

+    float v_0 = mix(v_0_0, v_0_1, delta.y);

+    float v_1 = mix(v_1_0, v_1_1, delta.y);

+    float v = mix(v_0, v_1, delta.z);

+    return v;

+}

+

+rs_allocation bricks;

+rs_allocation brick_index;

+int brick_dimx;

+int brick_dimy;

+int brick_dimz;

+

+static int isInBrick(int3 p) {

+    int bx = p.x >> 5;

+    int by = p.y >> 5;

+    int bz = p.z >> 5;

+    int brickno = bx + (by + brick_dimy * bz) * brick_dimx;

+

+    brickno *= 32 * 32;

+    int off = brickno + (p.z & 0x1F) * 32 + (p.y & 0x1F);

+    uint slice = rsGetElementAt_uint(bricks, off);

+    return slice & (1 << (p.x & 0x1F));

+}

+

+rs_allocation volume;

+rs_allocation zbuff;

+bool debug = true;

+

+uchar4 __attribute__ ((kernel)) draw_z_buffer(float2 in, uint32_t x, uint32_t y) {

+    uchar4 out = base_color;

+

+    float zsuface = in.x + .5f;

+    float zstart = zsuface;

+    float zend = in.y - 2.f;//0.5f;

+    float zlen = zend - zstart;

+    float step_dist = length(dz);

+

+    if (zstart == FLOAT_MAX || zlen < 0) {

+        return out;

+    }

+

+    float3 p = s + x * dx + y * dy + dz * zstart;

+    float zb = zend;

+    int izlen = (int) zlen;

+    float light = 1;

+    float4 total_color = (float4) {0.f, 0.f, 0.f, 0.f};

+

+    if (false) { // show the walls only used for debuging

+        int3 ip = convert_int3(p);

+        ip = clamp(ip, 4, 500);

+        short pix = rsGetElementAt_short(volume, ip.x, ip.y, ip.z);

+

+        int intensity = (((short) pix) & 0xFFFF);

+        //   intensity = clamp(intensity,0,400);

+        uchar4 color = rsGetElementAt_uchar4(color_map, intensity * 2);

+        int op = rsGetElementAt_uchar(opacity, intensity);

+

+        out.r = color.r;

+        out.g = color.g;

+        out.b = color.b;

+        out.a = 255;

+        return out;

+    }

+    {

+        int3 ip = convert_int3(p);

+

+        if (isInBrick(ip)) { // isInBrick(ip)) {

+

+            float3 delta = p - convert_float3(ip);

+            // TODO switch to rsAllocationVLoadX_short2

+            // short2 tmps = rsAllocationVLoadX_short2(volume, ip.x + 0, ip.y + 0, ip.z + 0);

+            //  float2 tmp =   convert_float2(tmps);

+            float v_0_0_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 0);

+            float v_0_0_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 0);

+

+            float v_0_1_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 0);

+            float v_0_1_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 0);

+            float v_1_0_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 1);

+            float v_1_0_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 1);

+            float v_1_1_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 1);

+            float v_1_1_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 1);

+            float v = triLinear(v_0_0_0, v_0_0_1, v_0_1_0, v_0_1_1,

+                    v_1_0_0, v_1_0_1, v_1_1_0, v_1_1_1,

+                    delta);

+            int intensity = (((short) v) & 0xFFFF);

+            uchar op = rsGetElementAt_uchar(opacity, intensity);

+

+            if (op != 0) { // near the surface "broken"

+

+                float sdx = rsGetElementAt_float2(zbuff, max(0, (int) x - 1), y).x - in.x;

+                float sdy = rsGetElementAt_float2(zbuff, x, max(0, (int) y - 1)).x - in.x;

+                float dot_prod = sqrt(1 / (1 + (sdy * sdy + sdx * sdx) * zoomFactor));

+                float opf = op / 255.f;

+                uchar4 color = rsGetElementAt_uchar4(color_map, intensity * 2);

+                uchar4 mat = rsGetElementAt_uchar4(color_map, intensity * 2 + 1);

+                float4 fcolor = convert_float4(color);;

+

+                float ambient = mat.x / 255.f;

+                float specular = mat.y / 255.f;

+                float diffuse = mat.z / 255.f;

+                float lop = (ambient + diffuse * dot_prod) * light * opf;

+                light -= opf;

+                total_color += fcolor * lop;

+                zb = zstart;

+

+            }

+        }

+    }

+    p += dz * rsRand(2.f);

+

+    if (light > 0) {

+        for (int k = 0; k < izlen - 1; k++) {

+

+            int3 ip = convert_int3(p);

+            if (isInBrick(ip)) {

+                float3 delta = p - convert_float3(ip);

+

+                float v_0_0_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 0);

+                float v_0_0_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 0);

+                float v_0_1_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 0);

+                float v_0_1_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 0);

+                float v_1_0_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 1);

+                float v_1_0_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 1);

+                float v_1_1_0 = (float) rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 1);

+                float v_1_1_1 = (float) rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 1);

+                float v = triLinear(v_0_0_0, v_0_0_1, v_0_1_0, v_0_1_1,

+                        v_1_0_0, v_1_0_1, v_1_1_0, v_1_1_1,

+                        delta);

+                int intensity = (((short) v) & 0xFFFF);

+                uchar op = rsGetElementAt_uchar(opacity, intensity);

+

+                if (op != 0) {

+

+                    float3 v;

+                    float3 vn;

+

+                    float v_0_0_2 = rsGetElementAt_short(volume, ip.x + 2, ip.y + 0, ip.z + 0);

+                    float v_0_1_2 = rsGetElementAt_short(volume, ip.x + 2, ip.y + 1, ip.z + 0);

+                    float v_1_0_2 = rsGetElementAt_short(volume, ip.x + 2, ip.y + 0, ip.z + 1);

+                    float v_1_1_2 = rsGetElementAt_short(volume, ip.x + 2, ip.y + 1, ip.z + 1);

+                    v.x = triLinear(v_0_0_1, v_0_0_2, v_0_1_1, v_0_1_2,

+                            v_1_0_1, v_1_0_2, v_1_1_1, v_1_1_2,

+                            delta);

+

+                    float v_0_0_n = rsGetElementAt_short(volume, ip.x - 1, ip.y + 0, ip.z + 0);

+                    float v_0_1_n = rsGetElementAt_short(volume, ip.x - 1, ip.y + 1, ip.z + 0);

+                    float v_1_0_n = rsGetElementAt_short(volume, ip.x - 1, ip.y + 0, ip.z + 1);

+                    float v_1_1_n = rsGetElementAt_short(volume, ip.x - 1, ip.y + 1, ip.z + 1);

+                    vn.x = triLinear(v_0_0_n, v_0_0_0, v_0_1_n, v_0_1_0,

+                            v_1_0_n, v_1_0_0, v_1_1_n, v_1_1_0,

+                            delta);

+

+                    float v_0_2_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 2, ip.z + 0);

+                    float v_0_2_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 2, ip.z + 0);

+                    float v_1_2_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 2, ip.z + 1);

+                    float v_1_2_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 2, ip.z + 1);

+                    v.y = triLinear(v_0_1_0, v_0_1_1, v_0_2_0, v_0_2_1,

+                            v_1_1_0, v_1_1_1, v_1_2_0, v_1_2_1,

+                            delta);

+

+                    float v_0_n_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 0);

+                    float v_0_n_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 0);

+                    float v_1_n_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 1);

+                    float v_1_n_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 1);

+                    vn.y = triLinear(v_0_n_0, v_0_n_1, v_0_0_0, v_0_0_1,

+                            v_1_n_0, v_1_n_1, v_1_0_0, v_1_0_1,

+                            delta);

+

+                    float v_n_0_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z - 1);

+                    float v_n_0_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z - 1);

+                    float v_n_1_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z - 1);

+                    float v_n_1_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z - 1);

+                    vn.z = triLinear(v_n_0_0, v_n_0_1, v_n_1_0, v_n_1_1,

+                            v_0_0_0, v_0_0_1, v_0_1_0, v_0_1_1,

+                            delta);

+

+                    float v_2_0_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 0, ip.z + 2);

+                    float v_2_0_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 0, ip.z + 2);

+                    float v_2_1_0 = rsGetElementAt_short(volume, ip.x + 0, ip.y + 1, ip.z + 2);

+                    float v_2_1_1 = rsGetElementAt_short(volume, ip.x + 1, ip.y + 1, ip.z + 2);

+                    v.z = triLinear(v_1_0_0, v_1_0_1, v_1_1_0, v_1_1_1,

+                            v_2_0_0, v_2_0_1, v_2_1_0, v_2_1_1,

+                            delta);

+

+                    float3 dv = normalize(v - vn);

+                    float dot_prod = dot(dv, dz);

+                    float opf = op / 255.f;

+

+                    uchar4 color = rsGetElementAt_uchar4(color_map, intensity * 2);

+                    uchar4 mat = rsGetElementAt_uchar4(color_map, intensity * 2 + 1);

+                    float4 fcolor = convert_float4(color);;

+

+                    // float3 mLight = (float3) {0,-1,0};

+                    float3 normal = dv;

+                    float3 r = 2 * normal * dot(mLight, normal) - mLight;

+                    float spec = dot(r, dz);

+

+                    // Eye point in this space is in the direction (0,0,-1)

+                    // Spec * Math.pow(R_z , P) lets use power == 2 (cheap)

+

+                    float ambient = mat.x / 255.f; // ambient

+                    float specular = mat.y / 255.f; // specular not used right now

+                    float diffuse = mat.z / 255.f; // diffuse

+                    float lop = (ambient + diffuse * dot_prod + specular * pow(spec, 10)) * light * opf;

+                    light -= opf;

+                    total_color += fcolor * lop;

+

+                    zb = zstart + k;

+                    if (light <= 0) {

+                        break;

+                    }

+                }

+            }

+

+            p += dz;

+        }

+    }

+

+    out = convert_uchar4(total_color);

+    out.a = 0xFF;

+

+    return out;

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderDicom.java b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderDicom.java
new file mode 100644
index 0000000..7ee2ac9
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderDicom.java
@@ -0,0 +1,620 @@
+/*

+ * Copyright (C) 2015 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.example.android.rs.vr.loaders;

+

+import android.renderscript.Allocation;

+import android.renderscript.RenderScript;

+import android.renderscript.Type;

+import android.util.Log;

+

+import com.example.android.rs.vr.engine.ScriptC_bricked;

+import com.example.android.rs.vr.engine.Volume;

+

+import java.io.File;

+import java.io.RandomAccessFile;

+import java.nio.ByteOrder;

+import java.nio.MappedByteBuffer;

+import java.nio.channels.FileChannel.MapMode;

+import java.util.Arrays;

+import java.util.Comparator;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.Vector;

+

+/**

+ * The simplest possible DICOM Reader.

+ * Will only read raw 16 bit dicom slices (the most common type)

+ * If the volume is compressed (usually JPEG2000) you need a decompression tool

+ *

+ * Note: All constants 0xNNNN, 0xNNNN are DICOM TAGS

+ * (see online documentation of DICOM standard)

+ */

+public class LoaderDicom {

+    private static final String LOGTAG = "ReadDicom";

+    String mName;

+    final boolean dbg = false;

+    private ByteOrder mByteOrder;

+    boolean explicit = true;

+    MappedByteBuffer mMappedByteBuffer;

+    long mFileLen;

+    private static final int MIN_VOLUME_SIZE = 20;

+    class Element {

+        int mGroup;

+        int mElement;

+        short mVR;

+        long mLength;

+        Object mValue;

+

+        @Override

+        public String toString() {

+            byte[] vrs = new byte[]{(byte) (mVR & 0xFF), (byte) ((mVR >> 8) & 0xFF)};

+            return Integer.toHexString(mGroup) + "," +

+                    Integer.toHexString(mElement) + "(" +

+                    new String(vrs) + ") [" + mLength + "] ";

+        }

+    }

+

+    static short vr(String v) {

+        byte[] b = v.getBytes();

+        return (short) (((b[1] & 0xFF) << 8) | (b[0] & 0xFF));

+    }

+

+    static final short OB = vr("OB");

+    static final short OW = vr("OW");

+    static final short OF = vr("OF");

+    static final short SQ = vr("SQ");

+    static final short UT = vr("UT");

+    static final short UN = vr("UN");

+    static final short DS = vr("DS");

+    static final short US = vr("US");

+    static final short AS = vr("AS");

+    static final short AT = vr("AT");

+    static final short CS = vr("CS");

+    static final short DA = vr("DA");

+    static final short DT = vr("DT");

+    static final short FL = vr("FL");

+    static final short FD = vr("FD");

+    static final short IS = vr("IS");

+    static final short LO = vr("LO");

+    static final short LT = vr("LT");

+    static final short PN = vr("PN");

+    static final short SH = vr("SH");

+    static final short SL = vr("SL");

+    static final short SS = vr("SS");

+    static final short ST = vr("ST");

+    static final short TM = vr("TM");

+    static final short UI = vr("UI");

+    static final short UL = vr("UL");

+    static final short AE = vr("AE");

+

+    static HashSet<Short> strVRs = new HashSet<Short>();

+

+    static {

+        short[] all = new short[]{

+                AE, AS, CS, DA, DS, DT, IS, LO, LT, PN, SH, ST, TM, UT

+        };

+        for (short anAll : all) {

+            strVRs.add(anAll);

+        }

+    }

+

+    boolean str(short vr) {

+        return strVRs.contains(vr);

+    }

+

+    boolean big(short vr) {

+        return OB == vr || OW == vr || OF == vr || SQ == vr || UT == vr || UN == vr;

+    }

+

+    class TagSet extends HashMap<Integer, Element> {

+        Element get(int group, int element) {

+            return get(tagInt(group, element));

+        }

+

+        void put(Element e) {

+            put(tagInt(e.mGroup, e.mElement), e);

+        }

+    }

+

+    static int tagInt(int g, int e) {

+        return (g << 16) | (e & 0xFFFF);

+    }

+

+    public static ByteOrder reverse(ByteOrder o) {

+        return (o == ByteOrder.LITTLE_ENDIAN) ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;

+    }

+

+    public TagSet read(File file, int[] tags) throws Exception {

+        mName = file.getName();

+        TagSet set = new TagSet();

+        HashSet<Integer> toAdd = new HashSet<Integer>();

+        for (int n : tags) {

+            toAdd.add(n);

+        }

+        RandomAccessFile f = new RandomAccessFile(file, "r");

+

+        mMappedByteBuffer = f.getChannel().map(MapMode.READ_ONLY, 0, mFileLen = f.length());

+        mMappedByteBuffer.position(132);

+        setOrder(ByteOrder.LITTLE_ENDIAN);

+        Element e = new Element();

+        boolean early = true;

+

+        while (mMappedByteBuffer.position() < mFileLen) {

+            int pos = mMappedByteBuffer.position();

+            int jump = (int) readTag(e);

+

+            if (early) {

+                if (e.mGroup > 255) {

+                    setOrder((mByteOrder == ByteOrder.LITTLE_ENDIAN) ?

+                            ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);

+                    mMappedByteBuffer.position(mMappedByteBuffer.position() - jump);

+                    readTag(e);

+                }

+            }

+

+            if (early && e.mGroup >= 8) {

+

+                early = false;

+            }

+            if (toAdd.contains(tagInt(e.mGroup, e.mElement))) {

+                readValue(e);

+                set.put(e);

+                if (e.mGroup == 0x7fe0 && e.mElement == 0x10) {

+                    return set;

+                }

+                e = new Element();

+

+            } else {

+                if (e.mGroup == 0x7fe0 && e.mElement == 0x10) {

+                    return set;

+                }

+

+                skipValue(e);

+            }

+        }

+        return set;

+    }

+

+    private long readTag(Element e) {

+        e.mGroup = mMappedByteBuffer.getShort() & 0xFFFF;

+        e.mElement = mMappedByteBuffer.getShort() & 0xFFFF;

+

+        if (e.mGroup == 0xFFFE && e.mElement == 0xE000) {

+            e.mLength = mMappedByteBuffer.getInt();

+            if (e.mLength == -1) {

+                e.mLength = 0;

+            }

+            e.mVR = vr("s<");

+            return 8;

+        }

+

+        if (explicit) {

+            e.mVR = mMappedByteBuffer.getShort();

+

+            if (big(e.mVR)) {

+                mMappedByteBuffer.getShort();

+                e.mLength = mMappedByteBuffer.getInt() & 0xFFFFFFFF;

+            } else {

+                e.mLength = mMappedByteBuffer.getShort() & 0xFFFF;

+            }

+        } else {

+            e.mVR = 0;

+            int len = mMappedByteBuffer.getInt();

+            e.mLength = (len) & 0xFFFFFFFFL;

+            if (0xFFFFFFFF == e.mLength) {

+                Log.v(LOGTAG, "undefined");

+                e.mLength = 0;

+            }

+        }

+        if (e.mLength == -1 || e.mLength == 65535) {

+            e.mLength = 0;

+        }

+        return 8;

+    }

+

+    private void skipValue(Element e) {

+        if (e.mLength == 0) {

+            return;

+        }

+        if (dbg && str(e.mVR)) {

+            mMappedByteBuffer.get(readBuff, 0, (int) (e.mLength));

+            e.mValue = new String(readBuff, 0, (int) (e.mLength));

+            //    Log.v(LOGTAG, e + "  " + e.mValue);

+        } else {

+            mMappedByteBuffer.position((int) (mMappedByteBuffer.position() + e.mLength));

+        }

+    }

+

+    byte[] readBuff = new byte[200];

+

+    private void readValue(Element e) {

+        if (str(e.mVR)) {

+            mMappedByteBuffer.get(readBuff, 0, (int) (e.mLength));

+            e.mValue = new String(readBuff, 0, (int) (e.mLength));

+        } else if (e.mVR == US) {

+            e.mValue = new Short(mMappedByteBuffer.getShort());

+        } else if (e.mVR == OW) {

+            if (e.mLength == -1) {

+                e.mLength = mFileLen - mMappedByteBuffer.position();

+            }

+            short[] s = new short[(int) (e.mLength / 2)];

+            mMappedByteBuffer.asShortBuffer().get(s);

+            e.mValue = s;

+        }

+

+    }

+

+    private void setOrder(ByteOrder order) {

+        mByteOrder = order;

+        mMappedByteBuffer.order(mByteOrder);

+    }

+

+    public static Volume buildVolume(String dirName) {

+        return buildVolume(new File(dirName));

+    }

+

+    public static Volume buildVolume(File dir) {

+        LoaderDicom d = new LoaderDicom();

+        int[] tags = new int[]{

+                tagInt(0x20, 0x32),

+                tagInt(0x20, 0x37),

+                tagInt(0x28, 0x10),

+                tagInt(0x28, 0x11),

+                tagInt(0x7fe0, 0x10)

+        };

+

+        File[] files = dir.listFiles();

+        Arrays.sort(files, new Comparator<File>() {

+

+            @Override

+            public int compare(File o1, File o2) {

+

+                return o1.getName().compareTo(o2.getName());

+            }

+        });

+        Volume v = new Volume();

+        int count = 0;

+        for (File file : files) {

+            if (file.isDirectory()) {

+                continue;

+            }

+            if (file.getName().equals(".DS_Store")) {

+                continue;

+            }

+            count++;

+        }

+        if (count < MIN_VOLUME_SIZE) {

+            return null;

+        }

+        v.mData = new short[count][];

+        v.mDimz = count;

+        count = 0;

+        for (File file : files) {

+            if (file.isDirectory()) {

+                continue;

+            }

+            if (file.getName().equals(".DS_Store")) {

+                continue;

+            }

+            try {

+                TagSet data = d.read(file, tags);

+                v.mData[count] = (short[]) data.get(0x7fe0, 0x10).mValue;

+                count++;

+                v.mDimx = (Short) data.get(0x28, 0x10).mValue;

+                v.mDimy = (Short) data.get(0x28, 0x11).mValue;

+            } catch (Exception e) {

+                Log.e(LOGTAG, "Failed to parse " + file.getPath());

+                e.printStackTrace();

+            }

+        }

+        return v;

+    }

+

+    /**

+     * This is a multi threaded volume loaded

+     * It creates 2xthe number of cores

+     * @param rs The renderscript context

+     * @param dir The directory containing the DICOM files

+     * @param listener The Listener to provide feedback to the UI on loading

+     * @return The Volume object loaded with the volume

+     */

+    public static Volume buildRSVolume(final RenderScript rs, File dir,

+                                       final VolumeLoader.ProgressListener listener) {

+        final int[] tags = new int[]{

+                tagInt(0x20, 0x32),

+                tagInt(0x20, 0x37),

+                tagInt(0x28, 0x10),

+                tagInt(0x28, 0x11),

+                tagInt(0x28, 0x30),

+                tagInt(0x7fe0, 0x10)

+        };

+

+        File[] files = dir.listFiles();

+        Arrays.sort(files, new Comparator<File>() {

+

+            @Override

+            public int compare(File o1, File o2) {

+

+                return o1.getName().compareTo(o2.getName());

+            }

+        });

+        final Volume v = new Volume();

+        int count = 0;

+

+

+        final Vector<File> toRun = new Vector<File>();

+        final HashMap<File, Integer> fileMap = new HashMap<File, Integer>();

+        for (File file : files) {

+            if (file.isDirectory()) {

+                continue;

+            }

+            if (file.getName().equals(".DS_Store")) {

+                continue;

+            }

+            toRun.add(file);

+            fileMap.put(file, count);

+            count++;

+        }

+        if (count < MIN_VOLUME_SIZE) {

+            return null;

+        }

+        v.mDimz = count;

+        if (listener != null) {

+            listener.progress(0, v.mDimx);

+        }

+        v.mVolumeAllocation = null;

+        final String []pixel_spacing = new String[count];

+        final String []slice_pos = new String[count];

+

+        final ScriptC_bricked scriptC_bricked = new ScriptC_bricked(rs);

+        int number_of_threads = 2 * Runtime.getRuntime().availableProcessors();

+        Thread[] t = new Thread[number_of_threads];

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

+

+            t[i] = new Thread() {

+                LoaderDicom d = new LoaderDicom();

+

+

+                private File getOne() {

+                    synchronized (toRun) {

+                        if (toRun.isEmpty()) {

+                            return null;

+                        }

+                        return toRun.remove(0);

+                    }

+                }

+

+                public void run() {

+                    File file;

+

+                    Allocation alloc_slice = null;

+

+                    while ((file = getOne()) != null) {

+                        int z = fileMap.get(file);

+                        try {

+                            TagSet data = d.read(file, tags);

+                            short[] slice = (short[]) data.get(0x7fe0, 0x10).mValue;

+                            short dimX = (Short) data.get(0x28, 0x10).mValue;

+                            short dimY = (Short) data.get(0x28, 0x11).mValue;

+                            String val;

+                            val = (String) data.get(0x28,0x30).mValue;

+                            pixel_spacing[z] = val;

+

+                            val = (String) data.get(0x20,0x32).mValue;

+                            slice_pos[z] = val;

+

+                            if (v.mDimx == -1) {

+                                v.mDimy = dimY;

+                                v.mDimx = dimX;

+                            }

+                            synchronized (v) {

+                                if (v.mVolumeAllocation == null) {

+                                    Type.Builder b = new Type.Builder(rs,

+                                            android.renderscript.Element.I16(rs));

+                                    b.setX(v.mDimx).setY(v.mDimy);

+                                    b.setZ(v.mDimz);

+                                    v.mVolumeAllocation = Allocation.createTyped(rs, b.create(),

+                                            Allocation.USAGE_SCRIPT);

+                                    scriptC_bricked.set_volume(v.mVolumeAllocation);

+                                }

+                            }

+

+                            if (alloc_slice == null) {

+                                Type.Builder b = new Type.Builder(rs,

+                                        android.renderscript.Element.I16(rs));

+                                b.setX(v.mDimx).setY(v.mDimy);

+                                alloc_slice = Allocation.createTyped(rs, b.create(),

+                                        Allocation.USAGE_SCRIPT);

+                            }

+                            if (listener != null) {

+                                listener.progress(z, v.mDimx);

+                            }

+                            int size = v.mDimy * v.mDimx;

+                            alloc_slice.copyFromUnchecked(slice);

+                            synchronized (v) {

+                                scriptC_bricked.set_z(z);

+                                scriptC_bricked.forEach_copy(alloc_slice);

+                            }

+

+                        } catch (Exception e) {

+                            e.printStackTrace();

+                        }

+                    }

+                    alloc_slice.destroy();

+                }

+            };

+            t[i].start();

+        }

+

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

+            try {

+                t[i].join();

+            } catch (InterruptedException e) {

+                e.printStackTrace();

+            }

+        }

+        String[]pss = pixel_spacing[0].split("\\\\");

+        String[]s1ps = slice_pos[0].split("\\\\");

+        String[]s2ps = slice_pos[1].split("\\\\");

+        float sx = Float.parseFloat(pss[0]);

+        float sy = Float.parseFloat(pss[1]);

+        double dzx = Double.parseDouble(s1ps[0]) - Double.parseDouble(s2ps[0]);

+        double dzy = Double.parseDouble(s1ps[1]) - Double.parseDouble(s2ps[1]);

+        double dzz = Double.parseDouble(s1ps[2]) - Double.parseDouble(s2ps[2]);

+        float sz = (float) Math.hypot(dzx,Math.hypot(dzy,dzz));

+        float min = Math.min(sx,Math.min(sy,sz));

+        v.mVoxelDim[0] = sx/min;

+        v.mVoxelDim[1] = sy/min;

+        v.mVoxelDim[2] = sz/min;

+        Log.v(LOGTAG,"LOADING DONE ....");

+        scriptC_bricked.destroy();

+        return v;

+    }

+

+    /**

+     * Single threaded version of the volume createor

+     * @param rs the renderscript context

+     * @param dir the directory containing the dicom files

+     * @param listener used to feed back status to progress listeners

+     * @return Built volume

+     */

+    public static Volume buildRSVolume2(final RenderScript rs, File dir,

+                                        VolumeLoader.ProgressListener listener) {

+        final int[] tags = new int[]{

+                tagInt(0x20, 0x32),

+                tagInt(0x20, 0x37),

+                tagInt(0x28, 0x10),

+                tagInt(0x28, 0x11),

+                tagInt(0x28, 0x30),

+                tagInt(0x7fe0, 0x10)

+        };

+        File[] files = dir.listFiles();

+        Arrays.sort(files, new Comparator<File>() {

+

+            @Override

+            public int compare(File o1, File o2) {

+

+                return o1.getName().compareTo(o2.getName());

+            }

+        });

+        Volume v = new Volume();

+        int count = 0;

+

+

+        final Vector<File> toRun = new Vector<File>();

+        final HashMap<File, Integer> fileMap = new HashMap<File, Integer>();

+        for (File file1 : files) {

+            if (file1.isDirectory()) {

+                continue;

+            }

+            if (file1.getName().equals(".DS_Store")) {

+                continue;

+            }

+            toRun.add(file1);

+            fileMap.put(file1, count);

+            count++;

+        }

+        if (count < 20) {

+            return null;

+        }

+        v.mDimz = count;

+        if (listener != null) {

+            listener.progress(0, v.mDimz);

+        }

+        v.mVolumeAllocation = null;

+        Allocation alloc_slice = null;

+        ScriptC_bricked scriptC_bricked = new ScriptC_bricked(rs);

+        LoaderDicom d = new LoaderDicom();

+        String pixel_spacing = null;

+        String slice1_pos = null;

+        String slice2_pos = null;

+        boolean slice_spacing_set = false;

+        int z = 0;

+        for (File file : toRun) {

+            try {

+                TagSet data = d.read(file, tags);

+                short[] slice = (short[]) data.get(0x7fe0, 0x10).mValue;

+                short mDimx = (Short) data.get(0x28, 0x10).mValue;

+                short mDimy = (Short) data.get(0x28, 0x11).mValue;

+                String val;

+                val = (String) data.get(0x28,0x30).mValue;

+                if (val != null && pixel_spacing==null) {

+                    pixel_spacing = val;

+                }

+                val = (String) data.get(0x20,0x32).mValue;

+                if (val != null) {

+                    if (slice1_pos == null) {

+                        slice1_pos = val;

+                    } else if (slice2_pos == null) {

+                        slice2_pos = val;

+                    }

+                }

+                if (v.mDimx == -1) {

+                    v.mDimy = mDimy;

+                    v.mDimx = mDimx;

+                }

+

+                if (v.mVolumeAllocation == null) {

+                    Type.Builder b = new Type.Builder(rs, android.renderscript.Element.I16(rs));

+                    b.setX(v.mDimx).setY(v.mDimy);

+                    alloc_slice = Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);

+                    b.setZ(v.mDimz);

+                    v.mVolumeAllocation = Allocation.createTyped(rs, b.create(),

+                            Allocation.USAGE_SCRIPT);

+                    scriptC_bricked.set_volume(v.mVolumeAllocation);

+

+                }

+                if (listener != null) {

+                    listener.progress(z, v.mDimz);

+                }

+

+                int size = v.mDimy * v.mDimx;

+                alloc_slice.copyFromUnchecked(slice);

+                scriptC_bricked.set_z(z);

+                scriptC_bricked.forEach_copy(alloc_slice);

+                z++;

+                if (!slice_spacing_set

+                        && pixel_spacing!=null

+                        && slice1_pos!=null

+                        && slice2_pos != null) {

+                    String[]pss = pixel_spacing.split("\\\\");

+                    String[]s1ps = slice1_pos.split("\\\\");

+                    String[]s2ps = slice2_pos.split("\\\\");

+                    float sx = Float.parseFloat(pss[0]);

+                    float sy = Float.parseFloat(pss[1]);

+                    double dzx = Double.parseDouble(s1ps[0]) - Double.parseDouble(s2ps[0]);

+                    double dzy = Double.parseDouble(s1ps[1]) - Double.parseDouble(s2ps[1]);

+                    double dzz = Double.parseDouble(s1ps[2]) - Double.parseDouble(s2ps[2]);

+                    float sz = (float) Math.hypot(dzx,Math.hypot(dzy,dzz));

+                    float min = Math.min(sx,Math.min(sy,sz));

+                    v.mVoxelDim[0] = sx/min;

+                    v.mVoxelDim[1] = sy/min;

+                    v.mVoxelDim[2] = sz/min;

+                    slice_spacing_set = true;

+                }

+            } catch (Exception e) {

+                e.printStackTrace();

+            }

+        }

+        Log.v(LOGTAG,"LOADING DONE ....");

+

+        alloc_slice.destroy();

+

+        scriptC_bricked.destroy();

+        return v;

+    }

+}

diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderRaw.java b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderRaw.java
new file mode 100644
index 0000000..c58d0ec
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/LoaderRaw.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.loaders;
+
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+import com.example.android.rs.vr.engine.ScriptC_bricked;
+import com.example.android.rs.vr.engine.Volume;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.Vector;
+
+/**
+ * Created by hoford on 2/2/15.
+ */
+public class LoaderRaw {
+    private static final String LOGTAG = "RawLoader";
+
+    /**
+     * This builds the volume based on a collection of raw image files
+     * @param rs The Renderscript context
+     * @param dir The directory containing the raw images
+     * @param prop property object containing information about the files
+     * @param listener To provide feedback
+     * @return The created volume
+     */
+    public static Volume buildRSVolume(final RenderScript rs, File dir, Properties prop,
+                                       final VolumeLoader.ProgressListener listener) {
+        String[] dim = prop.getProperty("dim").split("x");
+        Volume v = new Volume();
+        v.mDimx = Integer.parseInt(dim[0]);
+        v.mDimy = Integer.parseInt(dim[1]);
+        v.mDimz = Integer.parseInt(dim[2]);
+        String[] voxeldim = prop.getProperty("voxeldim").split(",");
+        v.mVoxelDim[0] = Float.parseFloat(voxeldim[0]);
+        v.mVoxelDim[1] = Float.parseFloat(voxeldim[1]);
+        v.mVoxelDim[2] = Float.parseFloat(voxeldim[2]);
+        Float min = Math.min(v.mVoxelDim[0], Math.min(v.mVoxelDim[1], v.mVoxelDim[2]));
+        v.mVoxelDim[0] /= min;
+        v.mVoxelDim[1] /= min;
+        v.mVoxelDim[2] /= min;
+        listener.progress(0, v.mDimz);
+        if (v.mDimz < 20) {
+            return null;
+        }
+        Log.v(LOGTAG, "Loading " + dir.getPath());
+        File[] f = dir.listFiles();
+        Log.v(LOGTAG, "dir contains " + f.length + " files");
+        Arrays.sort(f, new Comparator<File>() {
+
+            @Override
+            public int compare(File o1, File o2) {
+
+                return Integer.decode(o1.getName()).compareTo(Integer.decode(o2.getName()));
+            }
+        });
+
+        int count = 0;
+
+
+        final Vector<File> toRun = new Vector<File>();
+        final HashMap<File, Integer> fileMap = new HashMap<File, Integer>();
+        for (int i = 0; i < f.length; i++) {
+            if (f[i].isDirectory()) {
+                continue;
+            }
+
+            toRun.add(f[i]);
+            fileMap.put(f[i], count);
+            count++;
+        }
+
+        v.mDimz = count;
+        if (listener != null) {
+            listener.progress(0, v.mDimz);
+        }
+
+        v.mVolumeAllocation = null;
+        Allocation alloc_slice = null;
+        ScriptC_bricked scriptC_bricked = new ScriptC_bricked(rs);
+        FileInputStream inputStream;
+        String pixel_spacing = null;
+        String slice1_pos = null;
+        String slice2_pos = null;
+        boolean slice_spacing_set = false;
+        int z = 0;
+        for (File file : toRun) {
+            try {
+                inputStream = new FileInputStream(file);
+                MappedByteBuffer mbb = inputStream.getChannel().map(FileChannel.MapMode.READ_ONLY,
+                        0, v.mDimy * v.mDimx * 2);
+                short[] slice = new short[v.mDimy * v.mDimx];
+                mbb.asShortBuffer().get(slice);
+                inputStream.close();
+                mbb = null;
+                if (v.mVolumeAllocation == null) {
+                    Log.v(LOGTAG, "make Volume " + z);
+                    Type.Builder b = new Type.Builder(rs, android.renderscript.Element.I16(rs));
+                    b.setX(v.mDimx).setY(v.mDimy);
+                    alloc_slice = Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+                    b.setZ(v.mDimz);
+                    v.mVolumeAllocation = Allocation.createTyped(rs,
+                            b.create(), Allocation.USAGE_SCRIPT);
+                    scriptC_bricked.set_volume(v.mVolumeAllocation);
+
+                }
+                Log.v(LOGTAG, "LOAD SLICE " + z);
+                int size = v.mDimy * v.mDimx;
+                alloc_slice.copyFromUnchecked(slice);
+                scriptC_bricked.set_z(z);
+                scriptC_bricked.forEach_copy(alloc_slice);
+                z++;
+                if (listener != null) {
+                    listener.progress(z, v.mDimz);
+                }
+
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        rs.finish();
+        alloc_slice.destroy();
+        Log.v(LOGTAG,"LOADING DONE ....");
+
+        scriptC_bricked.destroy();
+        return v;
+    }
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/Mandelbulb.java b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/Mandelbulb.java
new file mode 100644
index 0000000..7d05891
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/Mandelbulb.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.loaders;
+
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+import com.example.android.rs.vr.engine.ScriptC_bricked;
+import com.example.android.rs.vr.engine.Volume;
+
+/**
+ * Provides a simple an example of a computed data set and allows the application to
+ * be run without any data sets.
+ */
+public class Mandelbulb {
+    private static final String LOGTAG = "RawLoader";
+    private static final String simpleLook = "simple";
+    private static final int[][] simpleOpacity = {{120, 0x0}, {126, 0xFF}};
+    private static final int[][] simpleColor = {
+            {200, 0x44AA44, 30, 70, 0},
+            {230, 0xAA44AA, 30, 70, 0},
+            {255, 0xAAAAAA, 30, 70, 0}};
+
+    private static final int SIZE = 512;
+    public static final String NAME = "A Mandelbulb";
+
+    public static Volume buildRSVolume(RenderScript rs,
+                                       final VolumeLoader.ProgressListener listener) {
+        ScriptC_bricked scriptC_bricked = new ScriptC_bricked(rs);
+
+        Volume v = new Volume();
+        v.mDimx = v.mDimy = v.mDimz = SIZE;
+        v.mVoxelDim[0] = v.mVoxelDim[1] = v.mVoxelDim[2] = 1.f;
+        v.addLook(simpleLook, simpleColor, simpleOpacity);
+
+        Type.Builder b = new Type.Builder(rs, android.renderscript.Element.I16(rs));
+        b.setX(v.mDimx).setY(v.mDimy);
+        Allocation tmp = Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+        b.setZ(v.mDimz);
+        b.setX(v.mDimx).setY(v.mDimy).setZ(v.mDimz);
+        v.mVolumeAllocation = Allocation.createTyped(rs, b.create(), Allocation.USAGE_SCRIPT);
+
+        scriptC_bricked.set_volume(v.mVolumeAllocation);
+        scriptC_bricked.set_size(SIZE);
+        long time = System.nanoTime();
+        for (int z = 0; z < v.mDimz; z++) {
+            scriptC_bricked.set_z(z);
+            scriptC_bricked.forEach_mandelbulb(tmp);
+            scriptC_bricked.forEach_copy(tmp);
+            rs.finish();
+            listener.progress(z, v.mDimz);
+        }
+
+        Log.v(LOGTAG, "compute Mandelbulb in" + ((System.nanoTime() - time) / 1E9f) + "seconds");
+        tmp.destroy();
+        scriptC_bricked.destroy();
+        return v;
+    }
+
+}
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/VolumeLoader.java b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/VolumeLoader.java
new file mode 100644
index 0000000..2321e29
--- /dev/null
+++ b/java/tests/VrDemo/src/com/example/android/rs/vr/loaders/VolumeLoader.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.vr.loaders;
+
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+import com.example.android.rs.vr.engine.Volume;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Properties;
+
+public class VolumeLoader {
+    private static final String LOGTAG = "VolumeLoader";
+    HashMap<String, Properties> map = new HashMap<String, Properties>();
+    File baseDir;
+    ProgressListener mListener;
+
+    public VolumeLoader(String dir) {
+        map.put(Mandelbulb.NAME,null);
+
+        baseDir = new File(dir);
+        if (!baseDir.exists()) {
+            Log.e(LOGTAG, "Directory: \""+dir+"\" does not exist ");
+            return;
+        }
+        Properties[] prop = getPropertyFiles(baseDir);
+        for (int i = 0; i < prop.length; i++) {
+            map.put(prop[i].getProperty("name"), prop[i]);
+        }
+
+    }
+
+    public String[] getNames() {
+       String [] ret = map.keySet().toArray(new String[map.size()]);
+        Arrays.sort(ret);
+        return ret;
+    }
+
+    public Volume getVolume(RenderScript rs, String name) {
+        if (name.equals(Mandelbulb.NAME)) {
+            return  Mandelbulb.buildRSVolume(rs,mListener);
+        }
+        Properties p = map.get(name);
+        if (p == null) {
+            Log.v(LOGTAG,"Could not find "+name);
+            return null;
+        }
+        String dir = p.getProperty("dir");
+        Log.v(LOGTAG,"dir ="+dir);
+
+        if ("dicom".equalsIgnoreCase(p.getProperty("format"))) {
+            Log.v(LOGTAG,"processing dicom");
+            Volume v = LoaderDicom.buildRSVolume(rs, new File(baseDir, dir), mListener);
+            String [] looks = p.getProperty("looks").split(",");
+            for (int j = 0; j < looks.length; j++) {
+                String look_color = p.getProperty(looks[j]+".color");
+                String look_opacity = p.getProperty(looks[j]+".opacity");
+                v.addLook(looks[j],look_color,look_opacity);
+            }
+            return v;
+        } else if ("raw".equalsIgnoreCase(p.getProperty("format"))) {
+            Log.v(LOGTAG,"processing dicom");
+            Volume v = LoaderRaw.buildRSVolume(rs, new File(baseDir, dir), p, mListener);
+            String [] looks = p.getProperty("looks").split(",");
+            for (int j = 0; j < looks.length; j++) {
+                String look_color = p.getProperty(looks[j]+".color");
+                String look_opacity = p.getProperty(looks[j]+".opacity");
+                v.addLook(looks[j],look_color,look_opacity);
+            }
+            return v;
+        }
+        Log.v(LOGTAG,"could recognize format");
+        return null;
+    }
+
+    static Properties[] getPropertyFiles(File dir) {
+
+        File[] f = dir.listFiles(new FilenameFilter() {
+
+            @Override
+            public boolean accept(File dir, String name) {
+                Log.v(LOGTAG, name);
+                return name.endsWith(".prop");
+            }
+        });
+        Properties[]ret = new Properties[f.length];
+        for (int i = 0; i < f.length; i++) {
+            Properties prop = new Properties();
+            ret[i] = prop;
+            try {
+                prop.load(new FileReader(f[i]));
+
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return ret;
+    }
+
+    public void setProgressListener(ProgressListener listener){
+        mListener = listener;
+    }
+
+    public static interface ProgressListener {
+        public void progress(int n, int total);
+    }
+}
diff --git a/rs.spec b/rs.spec
index 69b7e7f..841b89f 100644
--- a/rs.spec
+++ b/rs.spec
@@ -88,6 +88,18 @@
     sync
     }
 
+AllocationAdapterCreate {
+    direct
+    param RsType vtype
+    param RsAllocation baseAlloc
+    ret RsAllocation
+}
+
+AllocationAdapterOffset {
+    param RsAllocation alloc
+    param const uint32_t *offsets
+}
+
 ContextFinish {
     sync
     }
diff --git a/rsAllocation.cpp b/rsAllocation.cpp
index 9b83ebb..5712a17 100644
--- a/rsAllocation.cpp
+++ b/rsAllocation.cpp
@@ -41,6 +41,53 @@
     updateCache();
 }
 
+Allocation::Allocation(Context *rsc, const Allocation *alloc, const Type *type)
+    : ObjectBase(rsc) {
+
+    memset(&mHal, 0, sizeof(mHal));
+
+
+    mHal.state.baseAlloc = alloc;
+    mHal.state.type = type;
+    mHal.state.usageFlags = alloc->mHal.state.usageFlags;
+    mHal.state.mipmapControl = RS_ALLOCATION_MIPMAP_NONE;
+
+    setType(type);
+    updateCache();
+
+
+
+
+    struct Hal {
+        void * drv;
+
+        struct DrvState {
+            struct LodState {
+                void * mallocPtr;
+                size_t stride;
+                uint32_t dimX;
+                uint32_t dimY;
+                uint32_t dimZ;
+            } lod[android::renderscript::Allocation::MAX_LOD];
+            size_t faceOffset;
+            uint32_t lodCount;
+            uint32_t faceCount;
+
+            struct YuvState {
+                uint32_t shift;
+                uint32_t step;
+            } yuv;
+
+            int grallocFlags;
+            uint32_t dimArray[Type::mMaxArrays];
+        };
+        mutable DrvState drvState;
+
+    };
+    Hal mHal;
+
+}
+
 void Allocation::operator delete(void* ptr) {
     if (ptr) {
         Allocation *a = (Allocation*) ptr;
@@ -69,6 +116,27 @@
     return a;
 }
 
+Allocation * Allocation::createAdapter(Context *rsc, const Allocation *alloc, const Type *type) {
+    // Allocation objects must use allocator specified by the driver
+    void* allocMem = rsc->mHal.funcs.allocRuntimeMem(sizeof(Allocation), 0);
+
+    if (!allocMem) {
+        rsc->setError(RS_ERROR_FATAL_DRIVER, "Couldn't allocate memory for Allocation");
+        return nullptr;
+    }
+
+    Allocation *a = new (allocMem) Allocation(rsc, alloc, type);
+
+    if (!rsc->mHal.funcs.allocation.initAdapter(rsc, a)) {
+        rsc->setError(RS_ERROR_FATAL_DRIVER, "Allocation::Allocation, alloc failure");
+        delete a;
+        return nullptr;
+    }
+
+    return a;
+}
+
+
 void Allocation::updateCache() {
     const Type *type = mHal.state.type;
     mHal.state.yuv = type->getDimYuv();
@@ -786,6 +854,22 @@
     a->read(rsc, xoff, yoff, lod, face, w, h, data, sizeBytes, stride);
 }
 
+RsAllocation rsi_AllocationAdapterCreate(Context *rsc, RsType vwindow, RsAllocation vbase) {
+
+
+    Allocation * alloc = Allocation::createAdapter(rsc,
+            static_cast<Allocation *>(vbase), static_cast<Type *>(vwindow));
+    if (!alloc) {
+        return nullptr;
+    }
+    alloc->incUserRef();
+    return alloc;
+}
+
+void rsi_AllocationAdapterOffset(Context *rsc, RsAllocation va, const uint32_t *offsets, size_t len) {
+}
+
+
 }
 }
 
diff --git a/rsAllocation.h b/rsAllocation.h
index cf33556..3714852 100644
--- a/rsAllocation.h
+++ b/rsAllocation.h
@@ -69,6 +69,15 @@
             int32_t surfaceTextureID;
             ANativeWindowBuffer *nativeBuffer;
             int64_t timestamp;
+
+            // Allocation adapter state
+            const Allocation *baseAlloc;
+            uint32_t originX;
+            uint32_t originY;
+            uint32_t originZ;
+            uint32_t originLOD;
+            uint32_t originFace;
+            uint32_t originArray[Type::mMaxArrays];
         };
         State state;
 
@@ -90,6 +99,7 @@
             } yuv;
 
             int grallocFlags;
+            uint32_t dimArray[Type::mMaxArrays];
         };
         mutable DrvState drvState;
 
@@ -101,6 +111,9 @@
     static Allocation * createAllocation(Context *rsc, const Type *, uint32_t usages,
                                          RsAllocationMipmapControl mc = RS_ALLOCATION_MIPMAP_NONE,
                                          void *ptr = 0);
+    static Allocation * createAdapter(Context *rsc, const Allocation *alloc, const Type *type);
+
+
     virtual ~Allocation();
     void updateCache();
 
@@ -211,6 +224,7 @@
 private:
     void freeChildrenUnlocked();
     Allocation(Context *rsc, const Type *, uint32_t usages, RsAllocationMipmapControl mc, void *ptr);
+    Allocation(Context *rsc, const Allocation *, const Type *);
 
     uint32_t getPackedSize() const;
     static void writePackedData(Context *rsc, const Type *type, uint8_t *dst,
diff --git a/rsScriptGroup.cpp b/rsScriptGroup.cpp
index 45a53e4..d4a6cd1 100644
--- a/rsScriptGroup.cpp
+++ b/rsScriptGroup.cpp
@@ -308,7 +308,7 @@
             }
 
             for (auto nodeOutput : node->mOutputs) {
-                if (nodeOutput->mDstKernel.get() == kernel) {
+                if (nodeOutput->mSource.get() == kernel) {
                     aout = nodeOutput->mAlloc.get();
                 }
             }
diff --git a/rs_hal.h b/rs_hal.h
index b3c2e39..9a4e9a5 100644
--- a/rs_hal.h
+++ b/rs_hal.h
@@ -164,6 +164,7 @@
 
     struct {
         bool (*init)(const Context *rsc, Allocation *alloc, bool forceZero);
+        bool (*initAdapter)(const Context *rsc, Allocation *alloc);
         void (*destroy)(const Context *rsc, Allocation *alloc);
         uint32_t (*grallocBits)(const Context *rsc, Allocation *alloc);
 
@@ -243,6 +244,8 @@
         void (*generateMipmaps)(const Context *rsc, const Allocation *alloc);
 
         void (*updateCachedObject)(const Context *rsc, const Allocation *alloc, rs_allocation *obj);
+
+        void (*adapterOffset)(const Context *rsc, const Allocation *alloc);
     } allocation;
 
     struct {