Cycles: Fix wrong hair render results when using BVH motion steps
authorSergey Sharybin <sergey.vfx@gmail.com>
Wed, 15 Feb 2017 09:56:54 +0000 (10:56 +0100)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 15 Feb 2017 11:45:04 +0000 (12:45 +0100)
The issue here was mainly coming from minimal pixel width feature
which is quite commonly enabled in production shots.

This feature will use some probabilistic heuristic in the curve
intersection function to check whether we need to return intersection
or not. This probability is calculated for every intersection check.
Now, when we use multiple BVH nodes for curve primitives we increase
probability of that primitive to be considered a good intersection
for us. This is similar to increasing minimal width of curve.

What is worst here is that change in the intersection probability
fully depends on exact layout of BVH, meaning probability might
change differently depending on a view angle, the way how builder
binned the primitives and such. This makes it impossible to do
simple check like dividing probability by number of BVH steps.

Other solution might have been to split BVH into fully independent
trees, but that will increase memory usage of all the static
objects in the scenes, which is also not something desirable.

For now used most simple but robust approach: store BVH primitives
time and test it in curve intersection functions. This solves the
regression, but has two downsides:

- Uses more memory.

  which isn't surprising, and ANY solution to this problem will
  use more memory.

  What we still have to do is to avoid this memory increase for
  cases when we don't use BVH motion steps.

- Reduces number of maximum available textures on pre-kepler cards.

  There is not much we can do here, hardware gets old but we need
  to move forward on more modern hardware..

intern/cycles/bvh/bvh.cpp
intern/cycles/bvh/bvh.h
intern/cycles/bvh/bvh_build.cpp
intern/cycles/bvh/bvh_build.h
intern/cycles/kernel/geom/geom_curve.h
intern/cycles/kernel/kernel_textures.h
intern/cycles/render/mesh.cpp
intern/cycles/render/scene.h

index 874a4246d1d60887969c9eee7a5391ede022bb64..7e91140709cb3c23d5084c46cd99ed2d13497107 100644 (file)
@@ -81,6 +81,7 @@ void BVH::build(Progress& progress)
                           pack.prim_type,
                           pack.prim_index,
                           pack.prim_object,
+                          pack.prim_time,
                           params,
                           progress);
        BVHNode *root = bvh_build.run();
@@ -252,6 +253,7 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
        pack.prim_visibility.resize(prim_index_size);
        pack.prim_tri_verts.resize(prim_tri_verts_size);
        pack.prim_tri_index.resize(prim_index_size);
+       pack.prim_time.resize(prim_index_size);
        pack.nodes.resize(nodes_size);
        pack.leaf_nodes.resize(leaf_nodes_size);
        pack.object_node.resize(objects.size());
@@ -264,6 +266,7 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
        uint *pack_prim_tri_index = (pack.prim_tri_index.size())? &pack.prim_tri_index[0]: NULL;
        int4 *pack_nodes = (pack.nodes.size())? &pack.nodes[0]: NULL;
        int4 *pack_leaf_nodes = (pack.leaf_nodes.size())? &pack.leaf_nodes[0]: NULL;
+       float2 *pack_prim_time = (pack.prim_time.size())? &pack.prim_time[0]: NULL;
 
        /* merge */
        foreach(Object *ob, objects) {
@@ -309,6 +312,7 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
                        int *bvh_prim_type = &bvh->pack.prim_type[0];
                        uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
                        uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
+                       float2 *bvh_prim_time = &bvh->pack.prim_time[0];
 
                        for(size_t i = 0; i < bvh_prim_index_size; i++) {
                                if(bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
@@ -324,6 +328,7 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
                                pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
                                pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
                                pack_prim_object[pack_prim_index_offset] = 0;  // unused for instances
+                               pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
                                pack_prim_index_offset++;
                        }
                }
index 35f4d305883bdcccc485c12fab4f18a36ac02310..08f41fc736f25eab9aee9f7ed2236b797456cb52 100644 (file)
@@ -68,6 +68,8 @@ struct PackedBVH {
        array<int> prim_index;
        /* mapping from BVH primitive index, to the object id of that primitive. */
        array<int> prim_object;
+       /* Time range of BVH primitive. */
+       array<float2> prim_time;
 
        /* index of the root node. */
        int root_index;
index a2f8b33cb0bace2ba5aca60b73fc25b14b34d038..06dfe5e439b985618a656235eac2eeae604f395b 100644 (file)
@@ -93,12 +93,14 @@ BVHBuild::BVHBuild(const vector<Object*>& objects_,
                    array<int>& prim_type_,
                    array<int>& prim_index_,
                    array<int>& prim_object_,
+                   array<float2>& prim_time_,
                    const BVHParams& params_,
                    Progress& progress_)
  : objects(objects_),
    prim_type(prim_type_),
    prim_index(prim_index_),
    prim_object(prim_object_),
+   prim_time(prim_time_),
    params(params_),
    progress(progress_),
    progress_start_time(0.0),
@@ -475,6 +477,7 @@ BVHNode* BVHBuild::run()
        prim_type.resize(references.size());
        prim_index.resize(references.size());
        prim_object.resize(references.size());
+       prim_time.resize(references.size());
 
        /* build recursively */
        BVHNode *rootnode;
@@ -849,6 +852,7 @@ BVHNode *BVHBuild::create_object_leaf_nodes(const BVHReference *ref, int start,
                prim_type[start] = ref->prim_type();
                prim_index[start] = ref->prim_index();
                prim_object[start] = ref->prim_object();
+               prim_time[start] = make_float2(ref->time_from(), ref->time_to());
 
                uint visibility = objects[ref->prim_object()]->visibility;
                BVHNode *leaf_node =  new LeafNode(ref->bounds(), visibility, start, start+1);
@@ -896,6 +900,7 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
        vector<int, LeafStackAllocator> p_type[PRIMITIVE_NUM_TOTAL];
        vector<int, LeafStackAllocator> p_index[PRIMITIVE_NUM_TOTAL];
        vector<int, LeafStackAllocator> p_object[PRIMITIVE_NUM_TOTAL];
+       vector<float2, LeafStackAllocator> p_time[PRIMITIVE_NUM_TOTAL];
        vector<BVHReference, LeafReferenceStackAllocator> p_ref[PRIMITIVE_NUM_TOTAL];
 
        /* TODO(sergey): In theory we should be able to store references. */
@@ -918,6 +923,8 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
                        p_type[type_index].push_back(ref.prim_type());
                        p_index[type_index].push_back(ref.prim_index());
                        p_object[type_index].push_back(ref.prim_object());
+                       p_time[type_index].push_back(make_float2(ref.time_from(),
+                                                                ref.time_to()));
 
                        bounds[type_index].grow(ref.bounds());
                        visibility[type_index] |= objects[ref.prim_object()]->visibility;
@@ -947,9 +954,11 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
        vector<int, LeafStackAllocator> local_prim_type,
                                        local_prim_index,
                                        local_prim_object;
+       vector<float2, LeafStackAllocator> local_prim_time;
        local_prim_type.resize(num_new_prims);
        local_prim_index.resize(num_new_prims);
        local_prim_object.resize(num_new_prims);
+       local_prim_time.resize(num_new_prims);
        for(int i = 0; i < PRIMITIVE_NUM_TOTAL; ++i) {
                int num = (int)p_type[i].size();
                if(num != 0) {
@@ -962,6 +971,7 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
                                local_prim_type[index] = p_type[i][j];
                                local_prim_index[index] = p_index[i][j];
                                local_prim_object[index] = p_object[i][j];
+                               local_prim_time[index] = p_time[i][j];
                                if(params.use_unaligned_nodes && !alignment_found) {
                                        alignment_found =
                                                unaligned_heuristic.compute_aligned_space(p_ref[i][j],
@@ -1028,11 +1038,13 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
                                prim_type.reserve(reserve);
                                prim_index.reserve(reserve);
                                prim_object.reserve(reserve);
+                               prim_time.reserve(reserve);
                        }
 
                        prim_type.resize(range_end);
                        prim_index.resize(range_end);
                        prim_object.resize(range_end);
+                       prim_time.resize(range_end);
                }
                spatial_spin_lock.unlock();
 
@@ -1041,6 +1053,7 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
                        memcpy(&prim_type[start_index], &local_prim_type[0], new_leaf_data_size);
                        memcpy(&prim_index[start_index], &local_prim_index[0], new_leaf_data_size);
                        memcpy(&prim_object[start_index], &local_prim_object[0], new_leaf_data_size);
+                       memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2)*num_new_leaf_data);
                }
        }
        else {
@@ -1053,6 +1066,7 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
                        memcpy(&prim_type[start_index], &local_prim_type[0], new_leaf_data_size);
                        memcpy(&prim_index[start_index], &local_prim_index[0], new_leaf_data_size);
                        memcpy(&prim_object[start_index], &local_prim_object[0], new_leaf_data_size);
+                       memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2)*num_new_leaf_data);
                }
        }
 
index ee3cde66a2f139c85d506e283acabb906d29ed4e..19af9c62ecd16bb8d09d9c9c54eb07c3cc7143d7 100644 (file)
@@ -48,6 +48,7 @@ public:
                 array<int>& prim_type,
                 array<int>& prim_index,
                 array<int>& prim_object,
+                array<float2>& prim_time,
                 const BVHParams& params,
                 Progress& progress);
        ~BVHBuild();
@@ -112,6 +113,7 @@ protected:
        array<int>& prim_type;
        array<int>& prim_index;
        array<int>& prim_object;
+       array<float2>& prim_time;
 
        /* Build parameters. */
        BVHParams params;
index 9de335403ce238b0e8201b1dc07cd6f0bb3efbd5..c8749545cdd2cd4bb3bf056e34020e500e6fc12d 100644 (file)
@@ -229,6 +229,15 @@ ccl_device_forceinline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Inte
        float3 P, float3 dir, uint visibility, int object, int curveAddr, float time,int type, uint *lcg_state, float difl, float extmax)
 #endif
 {
+       const bool is_curve_primitive = (type & PRIMITIVE_CURVE);
+
+       if(!is_curve_primitive) {
+               const float2 prim_time = kernel_tex_fetch(__prim_time, curveAddr);
+               if(time < prim_time.x || time > prim_time.y) {
+                       return false;
+               }
+       }
+
        int segment = PRIMITIVE_UNPACK_SEGMENT(type);
        float epsilon = 0.0f;
        float r_st, r_en;
@@ -257,7 +266,7 @@ ccl_device_forceinline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Inte
 
 #ifdef __KERNEL_AVX2__
                avxf P_curve_0_1, P_curve_2_3;
-               if(type & PRIMITIVE_CURVE) {
+               if(is_curve_primitive) {
                        P_curve_0_1 = _mm256_loadu2_m128(&kg->__curve_keys.data[k0].x, &kg->__curve_keys.data[ka].x);
                        P_curve_2_3 = _mm256_loadu2_m128(&kg->__curve_keys.data[kb].x, &kg->__curve_keys.data[k1].x);
                }
@@ -268,7 +277,7 @@ ccl_device_forceinline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Inte
 #else  /* __KERNEL_AVX2__ */
                ssef P_curve[4];
 
-               if(type & PRIMITIVE_CURVE) {
+               if(is_curve_primitive) {
                        P_curve[0] = load4f(&kg->__curve_keys.data[ka].x);
                        P_curve[1] = load4f(&kg->__curve_keys.data[k0].x);
                        P_curve[2] = load4f(&kg->__curve_keys.data[k1].x);
@@ -363,7 +372,7 @@ ccl_device_forceinline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Inte
 
                float4 P_curve[4];
 
-               if(type & PRIMITIVE_CURVE) {
+               if(is_curve_primitive) {
                        P_curve[0] = kernel_tex_fetch(__curve_keys, ka);
                        P_curve[1] = kernel_tex_fetch(__curve_keys, k0);
                        P_curve[2] = kernel_tex_fetch(__curve_keys, k1);
@@ -689,6 +698,15 @@ ccl_device_forceinline bool bvh_curve_intersect(KernelGlobals *kg, Intersection
 #  define dot3(x, y) dot(x, y)
 #endif
 
+       const bool is_curve_primitive = (type & PRIMITIVE_CURVE);
+
+       if(!is_curve_primitive) {
+               const float2 prim_time = kernel_tex_fetch(__prim_time, curveAddr);
+               if(time < prim_time.x || time > prim_time.y) {
+                       return false;
+               }
+       }
+
        int segment = PRIMITIVE_UNPACK_SEGMENT(type);
        /* curve Intersection check */
        int flags = kernel_data.curve.curveflags;
@@ -703,7 +721,7 @@ ccl_device_forceinline bool bvh_curve_intersect(KernelGlobals *kg, Intersection
 #ifndef __KERNEL_SSE2__
        float4 P_curve[2];
 
-       if(type & PRIMITIVE_CURVE) {
+       if(is_curve_primitive) {
                P_curve[0] = kernel_tex_fetch(__curve_keys, k0);
                P_curve[1] = kernel_tex_fetch(__curve_keys, k1);
        }
@@ -738,7 +756,7 @@ ccl_device_forceinline bool bvh_curve_intersect(KernelGlobals *kg, Intersection
 #else
        ssef P_curve[2];
        
-       if(type & PRIMITIVE_CURVE) {
+       if(is_curve_primitive) {
                P_curve[0] = load4f(&kg->__curve_keys.data[k0].x);
                P_curve[1] = load4f(&kg->__curve_keys.data[k1].x);
        }
index 8d5bb75a428fdb4a542d6818a1ea4ac9e790ad84..cb1a3f40dee3ef17c6fbecf760e10b3f4f4c3268 100644 (file)
@@ -32,6 +32,7 @@ KERNEL_TEX(uint, texture_uint, __prim_visibility)
 KERNEL_TEX(uint, texture_uint, __prim_index)
 KERNEL_TEX(uint, texture_uint, __prim_object)
 KERNEL_TEX(uint, texture_uint, __object_node)
+KERNEL_TEX(float2, texture_float2, __prim_time)
 
 /* objects */
 KERNEL_TEX(float4, texture_float4, __objects)
@@ -177,7 +178,6 @@ KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_085)
 KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_086)
 KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_087)
 KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_088)
-KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_089)
 
 #  else
 /* bindless textures */
index c42b32919d4ef2499f7894d2266a329286bbaa03..42dd4da8d0cb0cc0b68cacea75439739fbfa86fa 100644 (file)
@@ -1873,6 +1873,10 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene *
                dscene->prim_object.reference((uint*)&pack.prim_object[0], pack.prim_object.size());
                device->tex_alloc("__prim_object", dscene->prim_object);
        }
+       if(pack.prim_time.size()) {
+               dscene->prim_time.reference((float2*)&pack.prim_time[0], pack.prim_time.size());
+               device->tex_alloc("__prim_time", dscene->prim_time);
+       }
 
        dscene->data.bvh.root = pack.root_index;
        dscene->data.bvh.use_qbvh = scene->params.use_qbvh;
@@ -2152,6 +2156,7 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
        device->tex_free(dscene->prim_visibility);
        device->tex_free(dscene->prim_index);
        device->tex_free(dscene->prim_object);
+       device->tex_free(dscene->prim_time);
        device->tex_free(dscene->tri_shader);
        device->tex_free(dscene->tri_vnormal);
        device->tex_free(dscene->tri_vindex);
@@ -2173,6 +2178,7 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
        dscene->prim_visibility.clear();
        dscene->prim_index.clear();
        dscene->prim_object.clear();
+       dscene->prim_time.clear();
        dscene->tri_shader.clear();
        dscene->tri_vnormal.clear();
        dscene->tri_vindex.clear();
index 8768682043f889fe7cca1da6a5e5df861c82bb47..9f398c444f4df659888d87a2ee00ae4a786159ab 100644 (file)
@@ -69,6 +69,7 @@ public:
        device_vector<uint> prim_visibility;
        device_vector<uint> prim_index;
        device_vector<uint> prim_object;
+       device_vector<float2> prim_time;
 
        /* mesh */
        device_vector<uint> tri_shader;