Fix T54202: Cycles crash rendering empty mesh volume after recent optimization.
[blender-staging.git] / intern / cycles / render / mesh.cpp
index 9692e684c0a959268920f28e55a3da660158ecb2..47d24970949eeef220f4df29d71565f62957239b 100644 (file)
  * limitations under the License.
  */
 
-#include "bvh.h"
-#include "bvh_build.h"
-
-#include "camera.h"
-#include "curves.h"
-#include "device.h"
-#include "graph.h"
-#include "shader.h"
-#include "light.h"
-#include "mesh.h"
-#include "nodes.h"
-#include "object.h"
-#include "scene.h"
-
-#include "osl_globals.h"
-
-#include "util_foreach.h"
-#include "util_logging.h"
-#include "util_progress.h"
-#include "util_set.h"
+#include "bvh/bvh.h"
+#include "bvh/bvh_build.h"
+
+#include "render/camera.h"
+#include "render/curves.h"
+#include "device/device.h"
+#include "render/graph.h"
+#include "render/shader.h"
+#include "render/light.h"
+#include "render/mesh.h"
+#include "render/nodes.h"
+#include "render/object.h"
+#include "render/scene.h"
+
+#include "kernel/osl/osl_globals.h"
+
+#include "subd/subd_split.h"
+#include "subd/subd_patch_table.h"
+
+#include "util/util_foreach.h"
+#include "util/util_logging.h"
+#include "util/util_progress.h"
+#include "util/util_set.h"
 
 CCL_NAMESPACE_BEGIN
 
@@ -46,6 +49,84 @@ void Mesh::Triangle::bounds_grow(const float3 *verts, BoundBox& bounds) const
        bounds.grow(verts[v[2]]);
 }
 
+void Mesh::Triangle::motion_verts(const float3 *verts,
+                                  const float3 *vert_steps,
+                                  size_t num_verts,
+                                  size_t num_steps,
+                                  float time,
+                                  float3 r_verts[3]) const
+{
+       /* Figure out which steps we need to fetch and their interpolation factor. */
+       const size_t max_step = num_steps - 1;
+       const size_t step = min((int)(time * max_step), max_step - 1);
+       const float t = time*max_step - step;
+       /* Fetch vertex coordinates. */
+       float3 curr_verts[3];
+       float3 next_verts[3];
+       verts_for_step(verts,
+                      vert_steps,
+                      num_verts,
+                      num_steps,
+                      step,
+                      curr_verts);
+       verts_for_step(verts,
+                      vert_steps,
+                      num_verts,
+                      num_steps,
+                      step + 1,
+                      next_verts);
+       /* Interpolate between steps. */
+       r_verts[0] = (1.0f - t)*curr_verts[0] + t*next_verts[0];
+       r_verts[1] = (1.0f - t)*curr_verts[1] + t*next_verts[1];
+       r_verts[2] = (1.0f - t)*curr_verts[2] + t*next_verts[2];
+}
+
+void Mesh::Triangle::verts_for_step(const float3 *verts,
+                                    const float3 *vert_steps,
+                                    size_t num_verts,
+                                    size_t num_steps,
+                                    size_t step,
+                                    float3 r_verts[3]) const
+{
+       const size_t center_step = ((num_steps - 1) / 2);
+       if(step == center_step) {
+               /* Center step: regular vertex location. */
+               r_verts[0] = verts[v[0]];
+               r_verts[1] = verts[v[1]];
+               r_verts[2] = verts[v[2]];
+       }
+       else {
+               /* Center step not stored in the attribute array array. */
+               if(step > center_step) {
+                       step--;
+               }
+               size_t offset = step * num_verts;
+               r_verts[0] = vert_steps[offset + v[0]];
+               r_verts[1] = vert_steps[offset + v[1]];
+               r_verts[2] = vert_steps[offset + v[2]];
+       }
+}
+
+float3 Mesh::Triangle::compute_normal(const float3 *verts) const
+{
+       const float3& v0 = verts[v[0]];
+       const float3& v1 = verts[v[1]];
+       const float3& v2 = verts[v[2]];
+       const float3 norm = cross(v1 - v0, v2 - v0);
+       const float normlen = len(norm);
+       if(normlen == 0.0f) {
+               return make_float3(1.0f, 0.0f, 0.0f);
+       }
+       return norm / normlen;
+}
+
+bool Mesh::Triangle::valid(const float3 *verts) const
+{
+       return isfinite3_safe(verts[v[0]]) &&
+              isfinite3_safe(verts[v[1]]) &&
+              isfinite3_safe(verts[v[2]]);
+}
+
 /* Curve */
 
 void Mesh::Curve::bounds_grow(const int k, const float3 *curve_keys, const float *curve_radius, BoundBox& bounds) const
@@ -101,6 +182,205 @@ void Mesh::Curve::bounds_grow(const int k,
        bounds.grow(upper, mr);
 }
 
+void Mesh::Curve::bounds_grow(float4 keys[4], BoundBox& bounds) const
+{
+       float3 P[4] = {
+               float4_to_float3(keys[0]),
+               float4_to_float3(keys[1]),
+               float4_to_float3(keys[2]),
+               float4_to_float3(keys[3]),
+       };
+
+       float3 lower;
+       float3 upper;
+
+       curvebounds(&lower.x, &upper.x, P, 0);
+       curvebounds(&lower.y, &upper.y, P, 1);
+       curvebounds(&lower.z, &upper.z, P, 2);
+
+       float mr = max(keys[1].w, keys[2].w);
+
+       bounds.grow(lower, mr);
+       bounds.grow(upper, mr);
+}
+
+void Mesh::Curve::motion_keys(const float3 *curve_keys,
+                              const float *curve_radius,
+                              const float3 *key_steps,
+                              size_t num_curve_keys,
+                              size_t num_steps,
+                              float time,
+                              size_t k0, size_t k1,
+                              float4 r_keys[2]) const
+{
+       /* Figure out which steps we need to fetch and their interpolation factor. */
+       const size_t max_step = num_steps - 1;
+       const size_t step = min((int)(time * max_step), max_step - 1);
+       const float t = time*max_step - step;
+       /* Fetch vertex coordinates. */
+       float4 curr_keys[2];
+       float4 next_keys[2];
+       keys_for_step(curve_keys,
+                     curve_radius,
+                     key_steps,
+                     num_curve_keys,
+                     num_steps,
+                     step,
+                     k0, k1,
+                     curr_keys);
+       keys_for_step(curve_keys,
+                     curve_radius,
+                     key_steps,
+                     num_curve_keys,
+                     num_steps,
+                     step + 1,
+                     k0, k1,
+                     next_keys);
+       /* Interpolate between steps. */
+       r_keys[0] = (1.0f - t)*curr_keys[0] + t*next_keys[0];
+       r_keys[1] = (1.0f - t)*curr_keys[1] + t*next_keys[1];
+}
+
+void Mesh::Curve::cardinal_motion_keys(const float3 *curve_keys,
+                                       const float *curve_radius,
+                                       const float3 *key_steps,
+                                       size_t num_curve_keys,
+                                       size_t num_steps,
+                                       float time,
+                                       size_t k0, size_t k1,
+                                       size_t k2, size_t k3,
+                                       float4 r_keys[4]) const
+{
+       /* Figure out which steps we need to fetch and their interpolation factor. */
+       const size_t max_step = num_steps - 1;
+       const size_t step = min((int)(time * max_step), max_step - 1);
+       const float t = time*max_step - step;
+       /* Fetch vertex coordinates. */
+       float4 curr_keys[4];
+       float4 next_keys[4];
+       cardinal_keys_for_step(curve_keys,
+                              curve_radius,
+                              key_steps,
+                              num_curve_keys,
+                              num_steps,
+                              step,
+                              k0, k1, k2, k3,
+                              curr_keys);
+       cardinal_keys_for_step(curve_keys,
+                              curve_radius,
+                              key_steps,
+                              num_curve_keys,
+                              num_steps,
+                              step + 1,
+                              k0, k1, k2, k3,
+                              next_keys);
+       /* Interpolate between steps. */
+       r_keys[0] = (1.0f - t)*curr_keys[0] + t*next_keys[0];
+       r_keys[1] = (1.0f - t)*curr_keys[1] + t*next_keys[1];
+       r_keys[2] = (1.0f - t)*curr_keys[2] + t*next_keys[2];
+       r_keys[3] = (1.0f - t)*curr_keys[3] + t*next_keys[3];
+}
+
+void Mesh::Curve::keys_for_step(const float3 *curve_keys,
+                                const float *curve_radius,
+                                const float3 *key_steps,
+                                size_t num_curve_keys,
+                                size_t num_steps,
+                                size_t step,
+                                size_t k0, size_t k1,
+                                float4 r_keys[2]) const
+{
+       k0 = max(k0, 0);
+       k1 = min(k1, num_keys - 1);
+       const size_t center_step = ((num_steps - 1) / 2);
+       if(step == center_step) {
+               /* Center step: regular key location. */
+               /* TODO(sergey): Consider adding make_float4(float3, float)
+                * function.
+                */
+               r_keys[0] = make_float4(curve_keys[first_key + k0].x,
+                                       curve_keys[first_key + k0].y,
+                                       curve_keys[first_key + k0].z,
+                                       curve_radius[first_key + k0]);
+               r_keys[1] = make_float4(curve_keys[first_key + k1].x,
+                                       curve_keys[first_key + k1].y,
+                                       curve_keys[first_key + k1].z,
+                                       curve_radius[first_key + k1]);
+       }
+       else {
+               /* Center step is not stored in this array. */
+               if(step > center_step) {
+                       step--;
+               }
+               const size_t offset = first_key + step * num_curve_keys;
+               r_keys[0] = make_float4(key_steps[offset + k0].x,
+                                       key_steps[offset + k0].y,
+                                       key_steps[offset + k0].z,
+                                       curve_radius[first_key + k0]);
+               r_keys[1] = make_float4(key_steps[offset + k1].x,
+                                       key_steps[offset + k1].y,
+                                       key_steps[offset + k1].z,
+                                       curve_radius[first_key + k1]);
+       }
+}
+
+void Mesh::Curve::cardinal_keys_for_step(const float3 *curve_keys,
+                                         const float *curve_radius,
+                                         const float3 *key_steps,
+                                         size_t num_curve_keys,
+                                         size_t num_steps,
+                                         size_t step,
+                                         size_t k0, size_t k1,
+                                         size_t k2, size_t k3,
+                                         float4 r_keys[4]) const
+{
+       k0 = max(k0, 0);
+       k3 = min(k3, num_keys - 1);
+       const size_t center_step = ((num_steps - 1) / 2);
+       if(step == center_step) {
+               /* Center step: regular key location. */
+               r_keys[0] = make_float4(curve_keys[first_key + k0].x,
+                                       curve_keys[first_key + k0].y,
+                                       curve_keys[first_key + k0].z,
+                                       curve_radius[first_key + k0]);
+               r_keys[1] = make_float4(curve_keys[first_key + k1].x,
+                                       curve_keys[first_key + k1].y,
+                                       curve_keys[first_key + k1].z,
+                                       curve_radius[first_key + k1]);
+               r_keys[2] = make_float4(curve_keys[first_key + k2].x,
+                                       curve_keys[first_key + k2].y,
+                                       curve_keys[first_key + k2].z,
+                                       curve_radius[first_key + k2]);
+               r_keys[3] = make_float4(curve_keys[first_key + k3].x,
+                                       curve_keys[first_key + k3].y,
+                                       curve_keys[first_key + k3].z,
+                                       curve_radius[first_key + k3]);
+       }
+       else {
+               /* Center step is not stored in this array. */
+               if(step > center_step) {
+                       step--;
+               }
+               const size_t offset = first_key + step * num_curve_keys;
+               r_keys[0] = make_float4(key_steps[offset + k0].x,
+                                       key_steps[offset + k0].y,
+                                       key_steps[offset + k0].z,
+                                       curve_radius[first_key + k0]);
+               r_keys[1] = make_float4(key_steps[offset + k1].x,
+                                       key_steps[offset + k1].y,
+                                       key_steps[offset + k1].z,
+                                       curve_radius[first_key + k1]);
+               r_keys[2] = make_float4(key_steps[offset + k2].x,
+                                       key_steps[offset + k2].y,
+                                       key_steps[offset + k2].z,
+                                       curve_radius[first_key + k2]);
+               r_keys[3] = make_float4(key_steps[offset + k3].x,
+                                       key_steps[offset + k3].y,
+                                       key_steps[offset + k3].z,
+                                       curve_radius[first_key + k3]);
+       }
+}
+
 /* SubdFace */
 
 float3 Mesh::SubdFace::normal(const Mesh *mesh) const
@@ -112,19 +392,12 @@ float3 Mesh::SubdFace::normal(const Mesh *mesh) const
        return safe_normalize(cross(v1 - v0, v2 - v0));
 }
 
-
 /* Mesh */
 
 NODE_DEFINE(Mesh)
 {
        NodeType* type = NodeType::add("mesh", create);
 
-       static NodeEnum displacement_method_enum;
-       displacement_method_enum.insert("bump", DISPLACE_BUMP);
-       displacement_method_enum.insert("true", DISPLACE_TRUE);
-       displacement_method_enum.insert("both", DISPLACE_BOTH);
-       SOCKET_ENUM(displacement_method, "Displacement Method", displacement_method_enum, DISPLACE_BUMP);
-
        SOCKET_UINT(motion_steps, "Motion Steps", 3);
        SOCKET_BOOLEAN(use_motion_blur, "Use Motion Blur", false);
 
@@ -163,6 +436,8 @@ Mesh::Mesh()
        face_offset = 0;
        corner_offset = 0;
 
+       attr_map_offset = 0;
+
        num_subd_verts = 0;
 
        attributes.triangle_mesh = this;
@@ -171,17 +446,23 @@ Mesh::Mesh()
 
        geometry_flags = GEOMETRY_NONE;
 
+       volume_isovalue = 0.001f;
        has_volume = false;
        has_surface_bssrdf = false;
 
        num_ngons = 0;
 
        subdivision_type = SUBDIVISION_NONE;
+       subd_params = NULL;
+
+       patch_table = NULL;
 }
 
 Mesh::~Mesh()
 {
        delete bvh;
+       delete patch_table;
+       delete subd_params;
 }
 
 void Mesh::resize_mesh(int numverts, int numtris)
@@ -253,7 +534,7 @@ void Mesh::reserve_subd_faces(int numfaces, int num_ngons_, int numcorners)
        subd_attributes.resize(true);
 }
 
-void Mesh::clear()
+void Mesh::clear(bool preserve_voxel_data)
 {
        /* clear all verts and triangles */
        verts.clear();
@@ -274,15 +555,24 @@ void Mesh::clear()
 
        num_subd_verts = 0;
 
-       attributes.clear();
+       subd_creases.clear();
+
        curve_attributes.clear();
        subd_attributes.clear();
+       attributes.clear(preserve_voxel_data);
+
        used_shaders.clear();
 
+       if(!preserve_voxel_data) {
+               geometry_flags = GEOMETRY_NONE;
+       }
+
        transform_applied = false;
        transform_negative_scaled = false;
        transform_normal = transform_identity();
-       geometry_flags = GEOMETRY_NONE;
+
+       delete patch_table;
+       patch_table = NULL;
 }
 
 int Mesh::split_vertex(int vertex)
@@ -292,17 +582,17 @@ int Mesh::split_vertex(int vertex)
 
        foreach(Attribute& attr, attributes.attributes) {
                if(attr.element == ATTR_ELEMENT_VERTEX) {
-                       vector<char> tmp(attr.data_sizeof());
-                       memcpy(&tmp[0], attr.data() + tmp.size()*vertex, tmp.size());
-                       attr.add(&tmp[0]);
+                       array<char> tmp(attr.data_sizeof());
+                       memcpy(tmp.data(), attr.data() + tmp.size()*vertex, tmp.size());
+                       attr.add(tmp.data());
                }
        }
 
        foreach(Attribute& attr, subd_attributes.attributes) {
                if(attr.element == ATTR_ELEMENT_VERTEX) {
-                       vector<char> tmp(attr.data_sizeof());
-                       memcpy(&tmp[0], attr.data() + tmp.size()*vertex, tmp.size());
-                       attr.add(&tmp[0]);
+                       array<char> tmp(attr.data_sizeof());
+                       memcpy(tmp.data(), attr.data() + tmp.size()*vertex, tmp.size());
+                       attr.add(tmp.data());
                }
        }
 
@@ -354,7 +644,7 @@ void Mesh::add_curve(int first_key, int shader)
 
 void Mesh::add_subd_face(int* corners, int num_corners, int shader_, bool smooth_)
 {
-       size_t start_corner = subd_face_corners.size();
+       int start_corner = subd_face_corners.size();
 
        for(int i = 0; i < num_corners; i++) {
                subd_face_corners.push_back_reserved(corners[i]);
@@ -388,7 +678,7 @@ void Mesh::compute_bounds()
                if(use_motion_blur && attr) {
                        size_t steps_size = verts.size() * (motion_steps - 1);
                        float3 *vert_steps = attr->data_float3();
-       
+
                        for(size_t i = 0; i < steps_size; i++)
                                bnds.grow(vert_steps[i]);
                }
@@ -397,7 +687,7 @@ void Mesh::compute_bounds()
                if(use_motion_blur && curve_attr) {
                        size_t steps_size = curve_keys.size() * (motion_steps - 1);
                        float3 *key_steps = curve_attr->data_float3();
-       
+
                        for(size_t i = 0; i < steps_size; i++)
                                bnds.grow(key_steps[i]);
                }
@@ -411,11 +701,11 @@ void Mesh::compute_bounds()
 
                        for(size_t i = 0; i < curve_keys_size; i++)
                                bnds.grow_safe(curve_keys[i], curve_radius[i]);
-                       
+
                        if(use_motion_blur && attr) {
                                size_t steps_size = verts.size() * (motion_steps - 1);
                                float3 *vert_steps = attr->data_float3();
-               
+
                                for(size_t i = 0; i < steps_size; i++)
                                        bnds.grow_safe(vert_steps[i]);
                        }
@@ -423,7 +713,7 @@ void Mesh::compute_bounds()
                        if(use_motion_blur && curve_attr) {
                                size_t steps_size = curve_keys.size() * (motion_steps - 1);
                                float3 *key_steps = curve_attr->data_float3();
-               
+
                                for(size_t i = 0; i < steps_size; i++)
                                        bnds.grow_safe(key_steps[i]);
                        }
@@ -438,43 +728,24 @@ void Mesh::compute_bounds()
        bounds = bnds;
 }
 
-static float3 compute_face_normal(const Mesh::Triangle& t, float3 *verts)
-{
-       float3 v0 = verts[t.v[0]];
-       float3 v1 = verts[t.v[1]];
-       float3 v2 = verts[t.v[2]];
-
-       float3 norm = cross(v1 - v0, v2 - v0);
-       float normlen = len(norm);
-
-       if(normlen == 0.0f)
-               return make_float3(1.0f, 0.0f, 0.0f);
-
-       return norm / normlen;
-}
-
 void Mesh::add_face_normals()
 {
        /* don't compute if already there */
        if(attributes.find(ATTR_STD_FACE_NORMAL))
                return;
-       
+
        /* get attributes */
        Attribute *attr_fN = attributes.add(ATTR_STD_FACE_NORMAL);
        float3 *fN = attr_fN->data_float3();
 
        /* compute face normals */
        size_t triangles_size = num_triangles();
-       bool flip = transform_negative_scaled;
 
        if(triangles_size) {
-               float3 *verts_ptr = &verts[0];
+               float3 *verts_ptr = verts.data();
 
                for(size_t i = 0; i < triangles_size; i++) {
-                       fN[i] = compute_face_normal(get_triangle(i), verts_ptr);
-
-                       if(flip)
-                               fN[i] = -fN[i];
+                       fN[i] = get_triangle(i).compute_normal(verts_ptr);
                }
        }
 
@@ -494,7 +765,7 @@ void Mesh::add_vertex_normals()
        size_t triangles_size = num_triangles();
 
        /* static vertex normals */
-       if(!attributes.find(ATTR_STD_VERTEX_NORMAL)) {
+       if(!attributes.find(ATTR_STD_VERTEX_NORMAL) && triangles_size) {
                /* get attributes */
                Attribute *attr_fN = attributes.find(ATTR_STD_FACE_NORMAL);
                Attribute *attr_vN = attributes.add(ATTR_STD_VERTEX_NORMAL);
@@ -505,17 +776,17 @@ void Mesh::add_vertex_normals()
                /* compute vertex normals */
                memset(vN, 0, verts.size()*sizeof(float3));
 
-               if(triangles_size) {
-
-                       for(size_t i = 0; i < triangles_size; i++)
-                               for(size_t j = 0; j < 3; j++)
-                                       vN[get_triangle(i).v[j]] += fN[i];
+               for(size_t i = 0; i < triangles_size; i++) {
+                       for(size_t j = 0; j < 3; j++) {
+                               vN[get_triangle(i).v[j]] += fN[i];
+                       }
                }
 
                for(size_t i = 0; i < verts_size; i++) {
                        vN[i] = normalize(vN[i]);
-                       if(flip)
+                       if(flip) {
                                vN[i] = -vN[i];
+                       }
                }
        }
 
@@ -523,7 +794,7 @@ void Mesh::add_vertex_normals()
        Attribute *attr_mP = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
        Attribute *attr_mN = attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
 
-       if(has_motion_blur() && attr_mP && !attr_mN) {
+       if(has_motion_blur() && attr_mP && !attr_mN && triangles_size) {
                /* create attribute */
                attr_mN = attributes.add(ATTR_STD_MOTION_VERTEX_NORMAL);
 
@@ -534,27 +805,85 @@ void Mesh::add_vertex_normals()
                        /* compute */
                        memset(mN, 0, verts.size()*sizeof(float3));
 
-                       if(triangles_size) {
-                               for(size_t i = 0; i < triangles_size; i++) {
-                                       for(size_t j = 0; j < 3; j++) {
-                                               float3 fN = compute_face_normal(get_triangle(i), mP);
-                                               mN[get_triangle(i).v[j]] += fN;
-                                       }
+                       for(size_t i = 0; i < triangles_size; i++) {
+                               for(size_t j = 0; j < 3; j++) {
+                                       float3 fN = get_triangle(i).compute_normal(mP);
+                                       mN[get_triangle(i).v[j]] += fN;
                                }
                        }
 
                        for(size_t i = 0; i < verts_size; i++) {
                                mN[i] = normalize(mN[i]);
-                               if(flip)
+                               if(flip) {
                                        mN[i] = -mN[i];
+                               }
+                       }
+               }
+       }
+
+       /* subd vertex normals */
+       if(!subd_attributes.find(ATTR_STD_VERTEX_NORMAL) && subd_faces.size()) {
+               /* get attributes */
+               Attribute *attr_vN = subd_attributes.add(ATTR_STD_VERTEX_NORMAL);
+               float3 *vN = attr_vN->data_float3();
+
+               /* compute vertex normals */
+               memset(vN, 0, verts.size()*sizeof(float3));
+
+               for(size_t i = 0; i < subd_faces.size(); i++) {
+                       SubdFace& face = subd_faces[i];
+                       float3 fN = face.normal(this);
+
+                       for(size_t j = 0; j < face.num_corners; j++) {
+                               size_t corner = subd_face_corners[face.start_corner+j];
+                               vN[corner] += fN;
+                       }
+               }
+
+               for(size_t i = 0; i < verts_size; i++) {
+                       vN[i] = normalize(vN[i]);
+                       if(flip) {
+                               vN[i] = -vN[i];
                        }
                }
        }
 }
 
+void Mesh::add_undisplaced()
+{
+       AttributeSet& attrs = (subdivision_type == SUBDIVISION_NONE) ? attributes : subd_attributes;
+
+       /* don't compute if already there */
+       if(attrs.find(ATTR_STD_POSITION_UNDISPLACED)) {
+               return;
+       }
+
+       /* get attribute */
+       Attribute *attr = attrs.add(ATTR_STD_POSITION_UNDISPLACED);
+       attr->flags |= ATTR_SUBDIVIDED;
+
+       float3 *data = attr->data_float3();
+
+       /* copy verts */
+       size_t size = attr->buffer_size(this, (subdivision_type == SUBDIVISION_NONE) ? ATTR_PRIM_TRIANGLE : ATTR_PRIM_SUBD);
+
+       /* Center points for ngons aren't stored in Mesh::verts but are included in size since they will be
+        * calculated later, we subtract them from size here so we don't have an overflow while copying.
+        */
+       size -= num_ngons * attr->data_sizeof();
+
+       if(size) {
+               memcpy(data, verts.data(), size);
+       }
+}
+
 void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal)
 {
        Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL);
+       if(attr_vN == NULL) {
+               /* Happens on objects with just hair. */
+               return;
+       }
 
        float3 *vN = attr_vN->data_float3();
        uint shader_id = 0;
@@ -562,7 +891,7 @@ void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal)
        bool last_smooth = false;
 
        size_t triangles_size = num_triangles();
-       int *shader_ptr = (shader.size())? &shader[0]: NULL;
+       int *shader_ptr = shader.data();
 
        bool do_transform = transform_applied;
        Transform ntfm = transform_normal;
@@ -574,7 +903,7 @@ void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal)
                        last_smooth = smooth[i];
                        Shader *shader = (last_shader < used_shaders.size()) ?
                                used_shaders[last_shader] : scene->default_surface;
-                       shader_id = scene->shader_manager->get_shader_id(shader, this, last_smooth);
+                       shader_id = scene->shader_manager->get_shader_id(shader, last_smooth);
                }
 
                tri_shader[i] = shader_id;
@@ -586,7 +915,7 @@ void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal)
                float3 vNi = vN[i];
 
                if(do_transform)
-                       vNi = normalize(transform_direction(&ntfm, vNi));
+                       vNi = safe_normalize(transform_direction(&ntfm, vNi));
 
                vnormal[i] = make_float4(vNi.x, vNi.y, vNi.z, 0.0f);
        }
@@ -602,7 +931,7 @@ void Mesh::pack_verts(const vector<uint>& tri_prim_index,
        size_t verts_size = verts.size();
 
        if(verts_size && subd_faces.size()) {
-               float2 *vert_patch_uv_ptr = &vert_patch_uv[0];
+               float2 *vert_patch_uv_ptr = vert_patch_uv.data();
 
                for(size_t i = 0; i < verts_size; i++) {
                        tri_patch_uv[i] = vert_patch_uv_ptr[i];
@@ -611,16 +940,14 @@ void Mesh::pack_verts(const vector<uint>& tri_prim_index,
 
        size_t triangles_size = num_triangles();
 
-       if(triangles_size) {
-               for(size_t i = 0; i < triangles_size; i++) {
-                       Triangle t = get_triangle(i);
-                       tri_vindex[i] = make_uint4(t.v[0] + vert_offset,
-                                                  t.v[1] + vert_offset,
-                                                  t.v[2] + vert_offset,
-                                                  tri_prim_index[i + tri_offset]);
+       for(size_t i = 0; i < triangles_size; i++) {
+               Triangle t = get_triangle(i);
+               tri_vindex[i] = make_uint4(t.v[0] + vert_offset,
+                                          t.v[1] + vert_offset,
+                                          t.v[2] + vert_offset,
+                                          tri_prim_index[i + tri_offset]);
 
-                       tri_patch[i] = (!subd_faces.size()) ? -1 : (triangle_patch[i]*8 + patch_offset);
-               }
+               tri_patch[i] = (!subd_faces.size()) ? -1 : (triangle_patch[i]*8 + patch_offset);
        }
 }
 
@@ -630,8 +957,8 @@ void Mesh::pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, s
 
        /* pack curve keys */
        if(curve_keys_size) {
-               float3 *keys_ptr = &curve_keys[0];
-               float *radius_ptr = &curve_radius[0];
+               float3 *keys_ptr = curve_keys.data();
+               float *radius_ptr = curve_radius.data();
 
                for(size_t i = 0; i < curve_keys_size; i++)
                        curve_key_co[i] = make_float4(keys_ptr[i].x, keys_ptr[i].y, keys_ptr[i].z, radius_ptr[i]);
@@ -640,20 +967,18 @@ void Mesh::pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, s
        /* pack curve segments */
        size_t curve_num = num_curves();
 
-       if(curve_num) {
-               for(size_t i = 0; i < curve_num; i++) {
-                       Curve curve = get_curve(i);
-                       int shader_id = curve_shader[i];
-                       Shader *shader = (shader_id < used_shaders.size()) ?
-                               used_shaders[shader_id] : scene->default_surface;
-                       shader_id = scene->shader_manager->get_shader_id(shader, this, false);
-
-                       curve_data[i] = make_float4(
-                               __int_as_float(curve.first_key + curvekey_offset),
-                               __int_as_float(curve.num_keys),
-                               __int_as_float(shader_id),
-                               0.0f);
-               }
+       for(size_t i = 0; i < curve_num; i++) {
+               Curve curve = get_curve(i);
+               int shader_id = curve_shader[i];
+               Shader *shader = (shader_id < used_shaders.size()) ?
+                       used_shaders[shader_id] : scene->default_surface;
+               shader_id = scene->shader_manager->get_shader_id(shader, false);
+
+               curve_data[i] = make_float4(
+                       __int_as_float(curve.first_key + curvekey_offset),
+                       __int_as_float(curve.num_keys),
+                       __int_as_float(shader_id),
+                       0.0f);
        }
 }
 
@@ -662,13 +987,30 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
        size_t num_faces = subd_faces.size();
        int ngons = 0;
 
-       if(num_faces) {
-               for(size_t f = 0; f < num_faces; f++) {
-                       SubdFace face = subd_faces[f];
+       for(size_t f = 0; f < num_faces; f++) {
+               SubdFace face = subd_faces[f];
+
+               if(face.is_quad()) {
+                       int c[4];
+                       memcpy(c, &subd_face_corners[face.start_corner], sizeof(int)*4);
+
+                       *(patch_data++) = c[0] + vert_offset;
+                       *(patch_data++) = c[1] + vert_offset;
+                       *(patch_data++) = c[2] + vert_offset;
+                       *(patch_data++) = c[3] + vert_offset;
 
-                       if(face.is_quad()) {
+                       *(patch_data++) = f+face_offset;
+                       *(patch_data++) = face.num_corners;
+                       *(patch_data++) = face.start_corner + corner_offset;
+                       *(patch_data++) = 0;
+               }
+               else {
+                       for(int i = 0; i < face.num_corners; i++) {
                                int c[4];
-                               memcpy(c, &subd_face_corners[face.start_corner], sizeof(int)*4);
+                               c[0] = subd_face_corners[face.start_corner + mod(i + 0, face.num_corners)];
+                               c[1] = subd_face_corners[face.start_corner + mod(i + 1, face.num_corners)];
+                               c[2] = verts.size() - num_subd_verts + ngons;
+                               c[3] = subd_face_corners[face.start_corner + mod(i - 1, face.num_corners)];
 
                                *(patch_data++) = c[0] + vert_offset;
                                *(patch_data++) = c[1] + vert_offset;
@@ -676,37 +1018,18 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
                                *(patch_data++) = c[3] + vert_offset;
 
                                *(patch_data++) = f+face_offset;
-                               *(patch_data++) = face.num_corners;
+                               *(patch_data++) = face.num_corners | (i << 16);
                                *(patch_data++) = face.start_corner + corner_offset;
-                               *(patch_data++) = 0;
+                               *(patch_data++) = subd_face_corners.size() + ngons + corner_offset;
                        }
-                       else {
-                               for(int i = 0; i < face.num_corners; i++) {
-                                       int c[4];
-                                       c[0] = subd_face_corners[face.start_corner + mod(i + 0, face.num_corners)];
-                                       c[1] = subd_face_corners[face.start_corner + mod(i + 1, face.num_corners)];
-                                       c[2] = verts.size() - num_subd_verts + ngons;
-                                       c[3] = subd_face_corners[face.start_corner + mod(i - 1, face.num_corners)];
-
-                                       *(patch_data++) = c[0] + vert_offset;
-                                       *(patch_data++) = c[1] + vert_offset;
-                                       *(patch_data++) = c[2] + vert_offset;
-                                       *(patch_data++) = c[3] + vert_offset;
-
-                                       *(patch_data++) = f+face_offset;
-                                       *(patch_data++) = face.num_corners | (i << 16);
-                                       *(patch_data++) = face.start_corner + corner_offset;
-                                       *(patch_data++) = subd_face_corners.size() + ngons + corner_offset;
-                               }
 
-                               ngons++;
-                       }
+                       ngons++;
                }
        }
 }
 
-
-void Mesh::compute_bvh(DeviceScene *dscene,
+void Mesh::compute_bvh(Device *device,
+                       DeviceScene *dscene,
                        SceneParams *params,
                        Progress *progress,
                        int n,
@@ -740,9 +1063,13 @@ void Mesh::compute_bvh(DeviceScene *dscene,
 
                        BVHParams bparams;
                        bparams.use_spatial_split = params->use_bvh_spatial_split;
-                       bparams.use_qbvh = params->use_qbvh;
+                       bparams.bvh_layout = BVHParams::best_bvh_layout(
+                               params->bvh_layout,
+                               device->info.bvh_layout_mask);
                        bparams.use_unaligned_nodes = dscene->data.bvh.have_curves &&
                                                      params->use_bvh_unaligned_nodes;
+                       bparams.num_motion_triangle_steps = params->num_bvh_time_steps;
+                       bparams.num_motion_curve_steps = params->num_bvh_time_steps;
 
                        delete bvh;
                        bvh = BVH::create(bparams, objects);
@@ -779,6 +1106,17 @@ bool Mesh::has_motion_blur() const
                 curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)));
 }
 
+bool Mesh::has_true_displacement() const
+{
+       foreach(Shader *shader, used_shaders) {
+               if(shader->has_displacement && shader->displacement_method != DISPLACE_BUMP) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 bool Mesh::need_build_bvh() const
 {
        return !transform_applied || has_surface_bssrdf;
@@ -798,14 +1136,12 @@ bool Mesh::is_instanced() const
 
 MeshManager::MeshManager()
 {
-       bvh = NULL;
        need_update = true;
        need_flags_update = true;
 }
 
 MeshManager::~MeshManager()
 {
-       delete bvh;
 }
 
 void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<AttributeRequestSet>& mesh_attributes)
@@ -831,9 +1167,10 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
                        OSLGlobals::Attribute osl_attr;
 
                        osl_attr.type = attr.type();
-                       osl_attr.elem = ATTR_ELEMENT_OBJECT;
+                       osl_attr.desc.element = ATTR_ELEMENT_OBJECT;
                        osl_attr.value = attr;
-                       osl_attr.offset = 0;
+                       osl_attr.desc.offset = 0;
+                       osl_attr.desc.flags = 0;
 
                        og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_TRIANGLE][attr.name()] = osl_attr;
                        og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_CURVE][attr.name()] = osl_attr;
@@ -853,9 +1190,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
                foreach(AttributeRequest& req, attributes.requests) {
                        OSLGlobals::Attribute osl_attr;
 
-                       if(req.triangle_element != ATTR_ELEMENT_NONE) {
-                               osl_attr.elem = req.triangle_element;
-                               osl_attr.offset = req.triangle_offset;
+                       if(req.triangle_desc.element != ATTR_ELEMENT_NONE) {
+                               osl_attr.desc = req.triangle_desc;
 
                                if(req.triangle_type == TypeDesc::TypeFloat)
                                        osl_attr.type = TypeDesc::TypeFloat;
@@ -875,9 +1211,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
                                }
                        }
 
-                       if(req.curve_element != ATTR_ELEMENT_NONE) {
-                               osl_attr.elem = req.curve_element;
-                               osl_attr.offset = req.curve_offset;
+                       if(req.curve_desc.element != ATTR_ELEMENT_NONE) {
+                               osl_attr.desc = req.curve_desc;
 
                                if(req.curve_type == TypeDesc::TypeFloat)
                                        osl_attr.type = TypeDesc::TypeFloat;
@@ -897,9 +1232,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
                                }
                        }
 
-                       if(req.subd_element != ATTR_ELEMENT_NONE) {
-                               osl_attr.elem = req.subd_element;
-                               osl_attr.offset = req.subd_offset;
+                       if(req.subd_desc.element != ATTR_ELEMENT_NONE) {
+                               osl_attr.desc = req.subd_desc;
 
                                if(req.subd_type == TypeDesc::TypeFloat)
                                        osl_attr.type = TypeDesc::TypeFloat;
@@ -927,39 +1261,33 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
 #endif
 }
 
-void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Scene *scene, vector<AttributeRequestSet>& mesh_attributes)
+void MeshManager::update_svm_attributes(Device *, DeviceScene *dscene, Scene *scene, vector<AttributeRequestSet>& mesh_attributes)
 {
        /* for SVM, the attributes_map table is used to lookup the offset of an
         * attribute, based on a unique shader attribute id. */
 
        /* compute array stride */
-       int attr_map_stride = 0;
+       int attr_map_size = 0;
 
-       for(size_t i = 0; i < scene->meshes.size(); i++)
-               attr_map_stride = max(attr_map_stride, (mesh_attributes[i].size() + 1)*ATTR_PRIM_TYPES);
+       for(size_t i = 0; i < scene->meshes.size(); i++) {
+               Mesh *mesh = scene->meshes[i];
+               mesh->attr_map_offset = attr_map_size;
+               attr_map_size += (mesh_attributes[i].size() + 1)*ATTR_PRIM_TYPES;
+       }
 
-       if(attr_map_stride == 0)
+       if(attr_map_size == 0)
                return;
-       
+
        /* create attribute map */
-       uint4 *attr_map = dscene->attributes_map.resize(attr_map_stride*scene->objects.size());
+       uint4 *attr_map = dscene->attributes_map.alloc(attr_map_size*scene->meshes.size());
        memset(attr_map, 0, dscene->attributes_map.size()*sizeof(uint));
 
-       for(size_t i = 0; i < scene->objects.size(); i++) {
-               Object *object = scene->objects[i];
-               Mesh *mesh = object->mesh;
-
-               /* find mesh attributes */
-               size_t j;
-
-               for(j = 0; j < scene->meshes.size(); j++)
-                       if(scene->meshes[j] == mesh)
-                               break;
-
-               AttributeRequestSet& attributes = mesh_attributes[j];
+       for(size_t i = 0; i < scene->meshes.size(); i++) {
+               Mesh *mesh = scene->meshes[i];
+               AttributeRequestSet& attributes = mesh_attributes[i];
 
                /* set object attributes */
-               int index = i*attr_map_stride;
+               int index = mesh->attr_map_offset;
 
                foreach(AttributeRequest& req, attributes.requests) {
                        uint id;
@@ -971,8 +1299,8 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
 
                        if(mesh->num_triangles()) {
                                attr_map[index].x = id;
-                               attr_map[index].y = req.triangle_element;
-                               attr_map[index].z = as_uint(req.triangle_offset);
+                               attr_map[index].y = req.triangle_desc.element;
+                               attr_map[index].z = as_uint(req.triangle_desc.offset);
 
                                if(req.triangle_type == TypeDesc::TypeFloat)
                                        attr_map[index].w = NODE_ATTR_FLOAT;
@@ -980,14 +1308,16 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
                                        attr_map[index].w = NODE_ATTR_MATRIX;
                                else
                                        attr_map[index].w = NODE_ATTR_FLOAT3;
+
+                               attr_map[index].w |= req.triangle_desc.flags << 8;
                        }
 
                        index++;
 
                        if(mesh->num_curves()) {
                                attr_map[index].x = id;
-                               attr_map[index].y = req.curve_element;
-                               attr_map[index].z = as_uint(req.curve_offset);
+                               attr_map[index].y = req.curve_desc.element;
+                               attr_map[index].z = as_uint(req.curve_desc.offset);
 
                                if(req.curve_type == TypeDesc::TypeFloat)
                                        attr_map[index].w = NODE_ATTR_FLOAT;
@@ -995,14 +1325,16 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
                                        attr_map[index].w = NODE_ATTR_MATRIX;
                                else
                                        attr_map[index].w = NODE_ATTR_FLOAT3;
+
+                               attr_map[index].w |= req.curve_desc.flags << 8;
                        }
 
                        index++;
 
                        if(mesh->subd_faces.size()) {
                                attr_map[index].x = id;
-                               attr_map[index].y = req.subd_element;
-                               attr_map[index].z = as_uint(req.subd_offset);
+                               attr_map[index].y = req.subd_desc.element;
+                               attr_map[index].z = as_uint(req.subd_desc.offset);
 
                                if(req.subd_type == TypeDesc::TypeFloat)
                                        attr_map[index].w = NODE_ATTR_FLOAT;
@@ -1010,13 +1342,15 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
                                        attr_map[index].w = NODE_ATTR_MATRIX;
                                else
                                        attr_map[index].w = NODE_ATTR_FLOAT3;
+
+                               attr_map[index].w |= req.subd_desc.flags << 8;
                        }
 
                        index++;
                }
 
                /* terminator */
-               for(int i = 0; i < ATTR_PRIM_TYPES; i++) {
+               for(int j = 0; j < ATTR_PRIM_TYPES; j++) {
                        attr_map[index].x = ATTR_STD_NONE;
                        attr_map[index].y = 0;
                        attr_map[index].z = 0;
@@ -1027,8 +1361,7 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
        }
 
        /* copy to device */
-       dscene->data.bvh.attributes_map_stride = attr_map_stride;
-       device->tex_alloc("__attributes_map", dscene->attributes_map);
+       dscene->attributes_map.copy_to_device();
 }
 
 static void update_attribute_element_size(Mesh *mesh,
@@ -1060,26 +1393,29 @@ static void update_attribute_element_size(Mesh *mesh,
 }
 
 static void update_attribute_element_offset(Mesh *mesh,
-                                            vector<float>& attr_float,
+                                            device_vector<float>& attr_float,
                                             size_t& attr_float_offset,
-                                            vector<float4>& attr_float3,
+                                            device_vector<float4>& attr_float3,
                                             size_t& attr_float3_offset,
-                                            vector<uchar4>& attr_uchar4,
+                                            device_vector<uchar4>& attr_uchar4,
                                             size_t& attr_uchar4_offset,
                                             Attribute *mattr,
                                             AttributePrimitive prim,
                                             TypeDesc& type,
-                                            int& offset,
-                                            AttributeElement& element)
+                                            AttributeDescriptor& desc)
 {
        if(mattr) {
                /* store element and type */
-               element = mattr->element;
+               desc.element = mattr->element;
+               desc.flags = mattr->flags;
                type = mattr->type;
 
                /* store attribute data in arrays */
                size_t size = mattr->element_size(mesh, prim);
 
+               AttributeElement& element = desc.element;
+               int& offset = desc.offset;
+
                if(mattr->element == ATTR_ELEMENT_VOXEL) {
                        /* store slot in offset value */
                        VoxelAttribute *voxel_data = mattr->data_voxel();
@@ -1089,7 +1425,7 @@ static void update_attribute_element_offset(Mesh *mesh,
                        uchar4 *data = mattr->data_uchar4();
                        offset = attr_uchar4_offset;
 
-                       assert(attr_uchar4.capacity() >= offset + size);
+                       assert(attr_uchar4.size() >= offset + size);
                        for(size_t k = 0; k < size; k++) {
                                attr_uchar4[offset+k] = data[k];
                        }
@@ -1099,7 +1435,7 @@ static void update_attribute_element_offset(Mesh *mesh,
                        float *data = mattr->data_float();
                        offset = attr_float_offset;
 
-                       assert(attr_float.capacity() >= offset + size);
+                       assert(attr_float.size() >= offset + size);
                        for(size_t k = 0; k < size; k++) {
                                attr_float[offset+k] = data[k];
                        }
@@ -1109,7 +1445,7 @@ static void update_attribute_element_offset(Mesh *mesh,
                        Transform *tfm = mattr->data_transform();
                        offset = attr_float3_offset;
 
-                       assert(attr_float3.capacity() >= offset + size * 4);
+                       assert(attr_float3.size() >= offset + size * 4);
                        for(size_t k = 0; k < size*4; k++) {
                                attr_float3[offset+k] = (&tfm->x)[k];
                        }
@@ -1119,7 +1455,7 @@ static void update_attribute_element_offset(Mesh *mesh,
                        float4 *data = mattr->data_float4();
                        offset = attr_float3_offset;
 
-                       assert(attr_float3.capacity() >= offset + size);
+                       assert(attr_float3.size() >= offset + size);
                        for(size_t k = 0; k < size; k++) {
                                attr_float3[offset+k] = data[k];
                        }
@@ -1128,7 +1464,11 @@ static void update_attribute_element_offset(Mesh *mesh,
 
                /* mesh vertex/curve index is global, not per object, so we sneak
                 * a correction for that in here */
-               if(element == ATTR_ELEMENT_VERTEX)
+               if(mesh->subdivision_type == Mesh::SUBDIVISION_CATMULL_CLARK && desc.flags & ATTR_SUBDIVIDED) {
+                       /* indices for subdivided attributes are retrieved
+                        * from patch table so no need for correction here*/
+               }
+               else if(element == ATTR_ELEMENT_VERTEX)
                        offset -= mesh->vert_offset;
                else if(element == ATTR_ELEMENT_VERTEX_MOTION)
                        offset -= mesh->vert_offset;
@@ -1153,8 +1493,8 @@ static void update_attribute_element_offset(Mesh *mesh,
        }
        else {
                /* attribute not found */
-               element = ATTR_ELEMENT_NONE;
-               offset = 0;
+               desc.element = ATTR_ELEMENT_NONE;
+               desc.offset = 0;
        }
 }
 
@@ -1216,9 +1556,9 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
                }
        }
 
-       vector<float> attr_float(attr_float_size);
-       vector<float4> attr_float3(attr_float3_size);
-       vector<uchar4> attr_uchar4(attr_uchar4_size);
+       dscene->attributes_float.alloc(attr_float_size);
+       dscene->attributes_float3.alloc(attr_float3_size);
+       dscene->attributes_uchar4.alloc(attr_uchar4_size);
 
        size_t attr_float_offset = 0;
        size_t attr_float3_offset = 0;
@@ -1237,34 +1577,31 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
                        Attribute *subd_mattr = mesh->subd_attributes.find(req);
 
                        update_attribute_element_offset(mesh,
-                                                       attr_float, attr_float_offset,
-                                                       attr_float3, attr_float3_offset,
-                                                       attr_uchar4, attr_uchar4_offset,
+                                                       dscene->attributes_float, attr_float_offset,
+                                                       dscene->attributes_float3, attr_float3_offset,
+                                                       dscene->attributes_uchar4, attr_uchar4_offset,
                                                        triangle_mattr,
                                                        ATTR_PRIM_TRIANGLE,
                                                        req.triangle_type,
-                                                       req.triangle_offset,
-                                                       req.triangle_element);
+                                                       req.triangle_desc);
 
                        update_attribute_element_offset(mesh,
-                                                       attr_float, attr_float_offset,
-                                                       attr_float3, attr_float3_offset,
-                                                       attr_uchar4, attr_uchar4_offset,
+                                                       dscene->attributes_float, attr_float_offset,
+                                                       dscene->attributes_float3, attr_float3_offset,
+                                                       dscene->attributes_uchar4, attr_uchar4_offset,
                                                        curve_mattr,
                                                        ATTR_PRIM_CURVE,
                                                        req.curve_type,
-                                                       req.curve_offset,
-                                                       req.curve_element);
+                                                       req.curve_desc);
 
                        update_attribute_element_offset(mesh,
-                                                       attr_float, attr_float_offset,
-                                                       attr_float3, attr_float3_offset,
-                                                       attr_uchar4, attr_uchar4_offset,
+                                                       dscene->attributes_float, attr_float_offset,
+                                                       dscene->attributes_float3, attr_float3_offset,
+                                                       dscene->attributes_uchar4, attr_uchar4_offset,
                                                        subd_mattr,
                                                        ATTR_PRIM_SUBD,
                                                        req.subd_type,
-                                                       req.subd_offset,
-                                                       req.subd_element);
+                                                       req.subd_desc);
 
                        if(progress.get_cancel()) return;
                }
@@ -1281,18 +1618,21 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
        /* copy to device */
        progress.set_status("Updating Mesh", "Copying Attributes to device");
 
-       if(attr_float.size()) {
-               dscene->attributes_float.copy(&attr_float[0], attr_float.size());
-               device->tex_alloc("__attributes_float", dscene->attributes_float);
+       if(dscene->attributes_float.size()) {
+               dscene->attributes_float.copy_to_device();
        }
-       if(attr_float3.size()) {
-               dscene->attributes_float3.copy(&attr_float3[0], attr_float3.size());
-               device->tex_alloc("__attributes_float3", dscene->attributes_float3);
+       if(dscene->attributes_float3.size()) {
+               dscene->attributes_float3.copy_to_device();
        }
-       if(attr_uchar4.size()) {
-               dscene->attributes_uchar4.copy(&attr_uchar4[0], attr_uchar4.size());
-               device->tex_alloc("__attributes_uchar4", dscene->attributes_uchar4);
+       if(dscene->attributes_uchar4.size()) {
+               dscene->attributes_uchar4.copy_to_device();
        }
+
+       if(progress.get_cancel()) return;
+
+       /* After mesh attributes and patch tables have been copied to device memory,
+        * we need to update offsets in the objects. */
+       scene->object_manager->device_update_mesh_offsets(device, dscene, scene);
 }
 
 void MeshManager::mesh_calc_offset(Scene *scene)
@@ -1327,13 +1667,19 @@ void MeshManager::mesh_calc_offset(Scene *scene)
                if(mesh->subd_faces.size()) {
                        Mesh::SubdFace& last = mesh->subd_faces[mesh->subd_faces.size()-1];
                        patch_size += (last.ptex_offset + last.num_ptex_faces()) * 8;
+
+                       /* patch tables are stored in same array so include them in patch_size */
+                       if(mesh->patch_table) {
+                               mesh->patch_table_offset = patch_size;
+                               patch_size += mesh->patch_table->total_size();
+                       }
                }
                face_size += mesh->subd_faces.size();
                corner_size += mesh->subd_face_corners.size();
        }
 }
 
-void MeshManager::device_update_mesh(Device *device,
+void MeshManager::device_update_mesh(Device *,
                                      DeviceScene *dscene,
                                      Scene *scene,
                                      bool for_displacement,
@@ -1358,6 +1704,12 @@ void MeshManager::device_update_mesh(Device *device,
                if(mesh->subd_faces.size()) {
                        Mesh::SubdFace& last = mesh->subd_faces[mesh->subd_faces.size()-1];
                        patch_size += (last.ptex_offset + last.num_ptex_faces()) * 8;
+
+                       /* patch tables are stored in same array so include them in patch_size */
+                       if(mesh->patch_table) {
+                               mesh->patch_table_offset = patch_size;
+                               patch_size += mesh->patch_table->total_size();
+                       }
                }
        }
 
@@ -1376,10 +1728,9 @@ void MeshManager::device_update_mesh(Device *device,
                }
        }
        else {
-               PackedBVH& pack = bvh->pack;
-               for(size_t i = 0; i < pack.prim_index.size(); ++i) {
-                       if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
-                               tri_prim_index[pack.prim_index[i]] = pack.prim_tri_index[i];
+               for(size_t i = 0; i < dscene->prim_index.size(); ++i) {
+                       if((dscene->prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
+                               tri_prim_index[dscene->prim_index[i]] = dscene->prim_tri_index[i];
                        }
                }
        }
@@ -1389,11 +1740,11 @@ void MeshManager::device_update_mesh(Device *device,
                /* normals */
                progress.set_status("Updating Mesh", "Computing normals");
 
-               uint *tri_shader = dscene->tri_shader.resize(tri_size);
-               float4 *vnormal = dscene->tri_vnormal.resize(vert_size);
-               uint4 *tri_vindex = dscene->tri_vindex.resize(tri_size);
-               uint *tri_patch = dscene->tri_patch.resize(tri_size);
-               float2 *tri_patch_uv = dscene->tri_patch_uv.resize(vert_size);
+               uint *tri_shader = dscene->tri_shader.alloc(tri_size);
+               float4 *vnormal = dscene->tri_vnormal.alloc(vert_size);
+               uint4 *tri_vindex = dscene->tri_vindex.alloc(tri_size);
+               uint *tri_patch = dscene->tri_patch.alloc(tri_size);
+               float2 *tri_patch_uv = dscene->tri_patch_uv.alloc(vert_size);
 
                foreach(Mesh *mesh, scene->meshes) {
                        mesh->pack_normals(scene,
@@ -1411,43 +1762,48 @@ void MeshManager::device_update_mesh(Device *device,
                /* vertex coordinates */
                progress.set_status("Updating Mesh", "Copying Mesh to device");
 
-               device->tex_alloc("__tri_shader", dscene->tri_shader);
-               device->tex_alloc("__tri_vnormal", dscene->tri_vnormal);
-               device->tex_alloc("__tri_vindex", dscene->tri_vindex);
-               device->tex_alloc("__tri_patch", dscene->tri_patch);
-               device->tex_alloc("__tri_patch_uv", dscene->tri_patch_uv);
+               dscene->tri_shader.copy_to_device();
+               dscene->tri_vnormal.copy_to_device();
+               dscene->tri_vindex.copy_to_device();
+               dscene->tri_patch.copy_to_device();
+               dscene->tri_patch_uv.copy_to_device();
        }
 
        if(curve_size != 0) {
                progress.set_status("Updating Mesh", "Copying Strands to device");
 
-               float4 *curve_keys = dscene->curve_keys.resize(curve_key_size);
-               float4 *curves = dscene->curves.resize(curve_size);
+               float4 *curve_keys = dscene->curve_keys.alloc(curve_key_size);
+               float4 *curves = dscene->curves.alloc(curve_size);
 
                foreach(Mesh *mesh, scene->meshes) {
                        mesh->pack_curves(scene, &curve_keys[mesh->curvekey_offset], &curves[mesh->curve_offset], mesh->curvekey_offset);
                        if(progress.get_cancel()) return;
                }
 
-               device->tex_alloc("__curve_keys", dscene->curve_keys);
-               device->tex_alloc("__curves", dscene->curves);
+               dscene->curve_keys.copy_to_device();
+               dscene->curves.copy_to_device();
        }
 
        if(patch_size != 0) {
                progress.set_status("Updating Mesh", "Copying Patches to device");
 
-               uint *patch_data = dscene->patches.resize(patch_size);
+               uint *patch_data = dscene->patches.alloc(patch_size);
 
                foreach(Mesh *mesh, scene->meshes) {
                        mesh->pack_patches(&patch_data[mesh->patch_offset], mesh->vert_offset, mesh->face_offset, mesh->corner_offset);
+
+                       if(mesh->patch_table) {
+                               mesh->patch_table->copy_adjusting_offsets(&patch_data[mesh->patch_table_offset], mesh->patch_table_offset);
+                       }
+
                        if(progress.get_cancel()) return;
                }
 
-               device->tex_alloc("__patches", dscene->patches);
+               dscene->patches.copy_to_device();
        }
 
        if(for_displacement) {
-               float4 *prim_tri_verts = dscene->prim_tri_verts.resize(tri_size * 3);
+               float4 *prim_tri_verts = dscene->prim_tri_verts.alloc(tri_size * 3);
                foreach(Mesh *mesh, scene->meshes) {
                        for(size_t i = 0; i < mesh->num_triangles(); ++i) {
                                Mesh::Triangle t = mesh->get_triangle(i);
@@ -1457,7 +1813,7 @@ void MeshManager::device_update_mesh(Device *device,
                                prim_tri_verts[offset + 2] = float3_to_float4(mesh->verts[t.v[2]]);
                        }
                }
-               device->tex_alloc("__prim_tri_verts", dscene->prim_tri_verts);
+               dscene->prim_tri_verts.copy_to_device();
        }
 }
 
@@ -1466,21 +1822,27 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene *
        /* bvh build */
        progress.set_status("Updating Scene BVH", "Building");
 
-       VLOG(1) << (scene->params.use_qbvh ? "Using QBVH optimization structure"
-                                          : "Using regular BVH optimization structure");
-
        BVHParams bparams;
        bparams.top_level = true;
-       bparams.use_qbvh = scene->params.use_qbvh;
+       bparams.bvh_layout = BVHParams::best_bvh_layout(
+               scene->params.bvh_layout,
+               device->info.bvh_layout_mask);
        bparams.use_spatial_split = scene->params.use_bvh_spatial_split;
        bparams.use_unaligned_nodes = dscene->data.bvh.have_curves &&
                                      scene->params.use_bvh_unaligned_nodes;
+       bparams.num_motion_triangle_steps = scene->params.num_bvh_time_steps;
+       bparams.num_motion_curve_steps = scene->params.num_bvh_time_steps;
 
-       delete bvh;
-       bvh = BVH::create(bparams, scene->objects);
+       VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout)
+               << " layout.";
+
+       BVH *bvh = BVH::create(bparams, scene->objects);
        bvh->build(progress);
 
-       if(progress.get_cancel()) return;
+       if(progress.get_cancel()) {
+               delete bvh;
+               return;
+       }
 
        /* copy to device */
        progress.set_status("Updating Scene BVH", "Copying BVH to device");
@@ -1488,57 +1850,69 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene *
        PackedBVH& pack = bvh->pack;
 
        if(pack.nodes.size()) {
-               dscene->bvh_nodes.reference((float4*)&pack.nodes[0], pack.nodes.size());
-               device->tex_alloc("__bvh_nodes", dscene->bvh_nodes);
+               dscene->bvh_nodes.steal_data(pack.nodes);
+               dscene->bvh_nodes.copy_to_device();
        }
        if(pack.leaf_nodes.size()) {
-               dscene->bvh_leaf_nodes.reference((float4*)&pack.leaf_nodes[0], pack.leaf_nodes.size());
-               device->tex_alloc("__bvh_leaf_nodes", dscene->bvh_leaf_nodes);
+               dscene->bvh_leaf_nodes.steal_data(pack.leaf_nodes);
+               dscene->bvh_leaf_nodes.copy_to_device();
        }
        if(pack.object_node.size()) {
-               dscene->object_node.reference((uint*)&pack.object_node[0], pack.object_node.size());
-               device->tex_alloc("__object_node", dscene->object_node);
+               dscene->object_node.steal_data(pack.object_node);
+               dscene->object_node.copy_to_device();
        }
        if(pack.prim_tri_index.size()) {
-               dscene->prim_tri_index.reference((uint*)&pack.prim_tri_index[0], pack.prim_tri_index.size());
-               device->tex_alloc("__prim_tri_index", dscene->prim_tri_index);
+               dscene->prim_tri_index.steal_data(pack.prim_tri_index);
+               dscene->prim_tri_index.copy_to_device();
        }
        if(pack.prim_tri_verts.size()) {
-               dscene->prim_tri_verts.reference((float4*)&pack.prim_tri_verts[0], pack.prim_tri_verts.size());
-               device->tex_alloc("__prim_tri_verts", dscene->prim_tri_verts);
+               dscene->prim_tri_verts.steal_data(pack.prim_tri_verts);
+               dscene->prim_tri_verts.copy_to_device();
        }
        if(pack.prim_type.size()) {
-               dscene->prim_type.reference((uint*)&pack.prim_type[0], pack.prim_type.size());
-               device->tex_alloc("__prim_type", dscene->prim_type);
+               dscene->prim_type.steal_data(pack.prim_type);
+               dscene->prim_type.copy_to_device();
        }
        if(pack.prim_visibility.size()) {
-               dscene->prim_visibility.reference((uint*)&pack.prim_visibility[0], pack.prim_visibility.size());
-               device->tex_alloc("__prim_visibility", dscene->prim_visibility);
+               dscene->prim_visibility.steal_data(pack.prim_visibility);
+               dscene->prim_visibility.copy_to_device();
        }
        if(pack.prim_index.size()) {
-               dscene->prim_index.reference((uint*)&pack.prim_index[0], pack.prim_index.size());
-               device->tex_alloc("__prim_index", dscene->prim_index);
+               dscene->prim_index.steal_data(pack.prim_index);
+               dscene->prim_index.copy_to_device();
        }
        if(pack.prim_object.size()) {
-               dscene->prim_object.reference((uint*)&pack.prim_object[0], pack.prim_object.size());
-               device->tex_alloc("__prim_object", dscene->prim_object);
+               dscene->prim_object.steal_data(pack.prim_object);
+               dscene->prim_object.copy_to_device();
+       }
+       if(pack.prim_time.size()) {
+               dscene->prim_time.steal_data(pack.prim_time);
+               dscene->prim_time.copy_to_device();
        }
 
        dscene->data.bvh.root = pack.root_index;
-       dscene->data.bvh.use_qbvh = scene->params.use_qbvh;
+       dscene->data.bvh.bvh_layout = bparams.bvh_layout;
+       dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
+
+       delete bvh;
 }
 
-void MeshManager::device_update_flags(Device * /*device*/,
-                                      DeviceScene * /*dscene*/,
-                                      Scene * scene,
-                                      Progress& /*progress*/)
+void MeshManager::device_update_preprocess(Device *device,
+                                           Scene *scene,
+                                           Progress& progress)
 {
        if(!need_update && !need_flags_update) {
                return;
        }
-       /* update flags */
+
+       progress.set_status("Updating Meshes Flags");
+
+       /* Update flags. */
+       bool volume_images_updated = false;
+
        foreach(Mesh *mesh, scene->meshes) {
                mesh->has_volume = false;
+
                foreach(const Shader *shader, mesh->used_shaders) {
                        if(shader->has_volume) {
                                mesh->has_volume = true;
@@ -1547,12 +1921,33 @@ void MeshManager::device_update_flags(Device * /*device*/,
                                mesh->has_surface_bssrdf = true;
                        }
                }
+
+               if(need_update && mesh->has_volume) {
+                       /* Create volume meshes if there is voxel data. */
+                       bool has_voxel_attributes = false;
+
+                       foreach(Attribute& attr, mesh->attributes.attributes) {
+                               if(attr.element == ATTR_ELEMENT_VOXEL) {
+                                       has_voxel_attributes = true;
+                               }
+                       }
+
+                       if(has_voxel_attributes) {
+                               if(!volume_images_updated) {
+                                       progress.set_status("Updating Meshes Volume Bounds");
+                                       device_update_volume_images(device, scene, progress);
+                                       volume_images_updated = true;
+                               }
+
+                               create_volume_mesh(scene, mesh, progress);
+                       }
+               }
        }
+
        need_flags_update = false;
 }
 
 void MeshManager::device_update_displacement_images(Device *device,
-                                                    DeviceScene *dscene,
                                                     Scene *scene,
                                                     Progress& progress)
 {
@@ -1563,22 +1958,14 @@ void MeshManager::device_update_displacement_images(Device *device,
        foreach(Mesh *mesh, scene->meshes) {
                if(mesh->need_update) {
                        foreach(Shader *shader, mesh->used_shaders) {
-                               if(shader->graph_bump == NULL) {
+                               if(!shader->has_displacement || shader->displacement_method == DISPLACE_BUMP) {
                                        continue;
                                }
-                               foreach(ShaderNode* node, shader->graph_bump->nodes) {
+                               foreach(ShaderNode* node, shader->graph->nodes) {
                                        if(node->special_type != SHADER_SPECIAL_TYPE_IMAGE_SLOT) {
                                                continue;
                                        }
-                                       if(device->info.pack_images) {
-                                               /* If device requires packed images we need to update all
-                                                * images now, even if they're not used for displacement.
-                                                */
-                                               image_manager->device_update(device,
-                                                                            dscene,
-                                                                            progress);
-                                               return;
-                                       }
+
                                        ImageSlotTextureNode *image_node = static_cast<ImageSlotTextureNode*>(node);
                                        int slot = image_node->slot;
                                        if(slot != -1) {
@@ -1592,13 +1979,51 @@ void MeshManager::device_update_displacement_images(Device *device,
                pool.push(function_bind(&ImageManager::device_update_slot,
                                        image_manager,
                                        device,
-                                       dscene,
+                                       scene,
                                        slot,
                                        &progress));
        }
        pool.wait_work();
 }
 
+void MeshManager::device_update_volume_images(Device *device,
+                                                                                         Scene *scene,
+                                                                                         Progress& progress)
+{
+       progress.set_status("Updating Volume Images");
+       TaskPool pool;
+       ImageManager *image_manager = scene->image_manager;
+       set<int> volume_images;
+
+       foreach(Mesh *mesh, scene->meshes) {
+               if(!mesh->need_update) {
+                       continue;
+               }
+
+               foreach(Attribute& attr, mesh->attributes.attributes) {
+                       if(attr.element != ATTR_ELEMENT_VOXEL) {
+                               continue;
+                       }
+
+                       VoxelAttribute *voxel = attr.data_voxel();
+
+                       if(voxel->slot != -1) {
+                               volume_images.insert(voxel->slot);
+                       }
+               }
+       }
+
+       foreach(int slot, volume_images) {
+               pool.push(function_bind(&ImageManager::device_update_slot,
+                                                               image_manager,
+                                                               device,
+                                                               scene,
+                                                               slot,
+                                                               &progress));
+       }
+       pool.wait_work();
+}
+
 void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
 {
        if(!need_update)
@@ -1609,7 +2034,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
        /* Update normals. */
        foreach(Mesh *mesh, scene->meshes) {
                foreach(Shader *shader, mesh->used_shaders) {
-                       if(shader->need_update_attributes)
+                       if(shader->need_update_mesh)
                                mesh->need_update = true;
                }
 
@@ -1617,6 +2042,46 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
                        mesh->add_face_normals();
                        mesh->add_vertex_normals();
 
+                       if(mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) {
+                               mesh->add_undisplaced();
+                       }
+
+                       if(progress.get_cancel()) return;
+               }
+       }
+
+       /* Tessellate meshes that are using subdivision */
+       size_t total_tess_needed = 0;
+       foreach(Mesh *mesh, scene->meshes) {
+               if(mesh->need_update &&
+                  mesh->subdivision_type != Mesh::SUBDIVISION_NONE &&
+                  mesh->num_subd_verts == 0 &&
+                  mesh->subd_params)
+               {
+                       total_tess_needed++;
+               }
+       }
+
+       size_t i = 0;
+       foreach(Mesh *mesh, scene->meshes) {
+               if(mesh->need_update &&
+                  mesh->subdivision_type != Mesh::SUBDIVISION_NONE &&
+                  mesh->num_subd_verts == 0 &&
+                  mesh->subd_params)
+               {
+                       string msg = "Tessellating ";
+                       if(mesh->name == "")
+                               msg += string_printf("%u/%u", (uint)(i+1), (uint)total_tess_needed);
+                       else
+                               msg += string_printf("%s %u/%u", mesh->name.c_str(), (uint)(i+1), (uint)total_tess_needed);
+
+                       progress.set_status("Updating Mesh", msg);
+
+                       DiagSplit dsplit(*mesh->subd_params);
+                       mesh->tessellate(&dsplit);
+
+                       i++;
+
                        if(progress.get_cancel()) return;
                }
        }
@@ -1626,7 +2091,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
        bool old_need_object_flags_update = false;
        foreach(Mesh *mesh, scene->meshes) {
                if(mesh->need_update &&
-                  mesh->displacement_method != Mesh::DISPLACE_BUMP)
+                  mesh->has_true_displacement())
                {
                        true_displacement_used = true;
                        break;
@@ -1634,7 +2099,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
        }
        if(true_displacement_used) {
                VLOG(1) << "Updating images used for true displacement.";
-               device_update_displacement_images(device, dscene, scene, progress);
+               device_update_displacement_images(device, scene, progress);
                old_need_object_flags_update = scene->object_manager->need_flags_update;
                scene->object_manager->device_update_flags(device,
                                                           dscene,
@@ -1677,7 +2142,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
        }
 
        /* Update bvh. */
-       size_t i = 0, num_bvh = 0;
+       size_t num_bvh = 0;
        foreach(Mesh *mesh, scene->meshes) {
                if(mesh->need_update && mesh->need_build_bvh()) {
                        num_bvh++;
@@ -1686,10 +2151,12 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
 
        TaskPool pool;
 
+       i = 0;
        foreach(Mesh *mesh, scene->meshes) {
                if(mesh->need_update) {
                        pool.push(function_bind(&Mesh::compute_bvh,
                                                mesh,
+                                               device,
                                                dscene,
                                                &scene->params,
                                                &progress,
@@ -1707,15 +2174,11 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
                << summary.full_report();
 
        foreach(Shader *shader, scene->shaders) {
-               shader->need_update_attributes = false;
+               shader->need_update_mesh = false;
        }
 
-#ifdef __OBJECT_MOTION__
-       Scene::MotionType need_motion = scene->need_motion(device->info.advanced_shading);
+       Scene::MotionType need_motion = scene->need_motion();
        bool motion_blur = need_motion == Scene::MOTION_BLUR;
-#else
-       bool motion_blur = false;
-#endif
 
        /* Update objects. */
        vector<Object *> volume_objects;
@@ -1746,48 +2209,28 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
 
 void MeshManager::device_free(Device *device, DeviceScene *dscene)
 {
-       device->tex_free(dscene->bvh_nodes);
-       device->tex_free(dscene->bvh_leaf_nodes);
-       device->tex_free(dscene->object_node);
-       device->tex_free(dscene->prim_tri_verts);
-       device->tex_free(dscene->prim_tri_index);
-       device->tex_free(dscene->prim_type);
-       device->tex_free(dscene->prim_visibility);
-       device->tex_free(dscene->prim_index);
-       device->tex_free(dscene->prim_object);
-       device->tex_free(dscene->tri_shader);
-       device->tex_free(dscene->tri_vnormal);
-       device->tex_free(dscene->tri_vindex);
-       device->tex_free(dscene->tri_patch);
-       device->tex_free(dscene->tri_patch_uv);
-       device->tex_free(dscene->curves);
-       device->tex_free(dscene->curve_keys);
-       device->tex_free(dscene->patches);
-       device->tex_free(dscene->attributes_map);
-       device->tex_free(dscene->attributes_float);
-       device->tex_free(dscene->attributes_float3);
-       device->tex_free(dscene->attributes_uchar4);
-
-       dscene->bvh_nodes.clear();
-       dscene->object_node.clear();
-       dscene->prim_tri_verts.clear();
-       dscene->prim_tri_index.clear();
-       dscene->prim_type.clear();
-       dscene->prim_visibility.clear();
-       dscene->prim_index.clear();
-       dscene->prim_object.clear();
-       dscene->tri_shader.clear();
-       dscene->tri_vnormal.clear();
-       dscene->tri_vindex.clear();
-       dscene->tri_patch.clear();
-       dscene->tri_patch_uv.clear();
-       dscene->curves.clear();
-       dscene->curve_keys.clear();
-       dscene->patches.clear();
-       dscene->attributes_map.clear();
-       dscene->attributes_float.clear();
-       dscene->attributes_float3.clear();
-       dscene->attributes_uchar4.clear();
+       dscene->bvh_nodes.free();
+       dscene->bvh_leaf_nodes.free();
+       dscene->object_node.free();
+       dscene->prim_tri_verts.free();
+       dscene->prim_tri_index.free();
+       dscene->prim_type.free();
+       dscene->prim_visibility.free();
+       dscene->prim_index.free();
+       dscene->prim_object.free();
+       dscene->prim_time.free();
+       dscene->tri_shader.free();
+       dscene->tri_vnormal.free();
+       dscene->tri_vindex.free();
+       dscene->tri_patch.free();
+       dscene->tri_patch_uv.free();
+       dscene->curves.free();
+       dscene->curve_keys.free();
+       dscene->patches.free();
+       dscene->attributes_map.free();
+       dscene->attributes_float.free();
+       dscene->attributes_float3.free();
+       dscene->attributes_uchar4.free();
 
 #ifdef WITH_OSL
        OSLGlobals *og = (OSLGlobals*)device->osl_memory();
@@ -1797,6 +2240,8 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
                og->attribute_map.clear();
                og->object_names.clear();
        }
+#else
+       (void)device;
 #endif
 }
 
@@ -1810,14 +2255,14 @@ bool Mesh::need_attribute(Scene *scene, AttributeStandard std)
 {
        if(std == ATTR_STD_NONE)
                return false;
-       
+
        if(scene->need_global_attribute(std))
                return true;
 
        foreach(Shader *shader, used_shaders)
                if(shader->attributes.find(std))
                        return true;
-       
+
        return false;
 }
 
@@ -1829,9 +2274,8 @@ bool Mesh::need_attribute(Scene * /*scene*/, ustring name)
        foreach(Shader *shader, used_shaders)
                if(shader->attributes.find(name))
                        return true;
-       
+
        return false;
 }
 
 CCL_NAMESPACE_END
-