Blender 2.78c: Fix wrong cycles hair render results when using BVH motion steps
This commit contains all commits required to get proper hair rendering with BVH motion steps enabled. 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..
This commit is contained in:
parent
c6e4a81dfb
commit
b3b4084073
|
@ -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();
|
||||
|
@ -256,6 +257,10 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
|||
pack.leaf_nodes.resize(leaf_nodes_size);
|
||||
pack.object_node.resize(objects.size());
|
||||
|
||||
if(params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) {
|
||||
pack.prim_time.resize(prim_index_size);
|
||||
}
|
||||
|
||||
int *pack_prim_index = (pack.prim_index.size())? &pack.prim_index[0]: NULL;
|
||||
int *pack_prim_type = (pack.prim_type.size())? &pack.prim_type[0]: NULL;
|
||||
int *pack_prim_object = (pack.prim_object.size())? &pack.prim_object[0]: NULL;
|
||||
|
@ -264,6 +269,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 +315,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.size()? &bvh->pack.prim_time[0]: NULL;
|
||||
|
||||
for(size_t i = 0; i < bvh_prim_index_size; i++) {
|
||||
if(bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
|
||||
|
@ -324,6 +331,9 @@ 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
|
||||
if(bvh_prim_time != NULL) {
|
||||
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
|
||||
}
|
||||
pack_prim_index_offset++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
@ -465,6 +467,9 @@ BVHNode* BVHBuild::run()
|
|||
}
|
||||
spatial_free_index = 0;
|
||||
|
||||
need_prim_time = params.num_motion_curve_steps > 0 ||
|
||||
params.num_motion_triangle_steps > 0;
|
||||
|
||||
/* init progress updates */
|
||||
double build_start_time;
|
||||
build_start_time = progress_start_time = time_dt();
|
||||
|
@ -475,6 +480,12 @@ BVHNode* BVHBuild::run()
|
|||
prim_type.resize(references.size());
|
||||
prim_index.resize(references.size());
|
||||
prim_object.resize(references.size());
|
||||
if(need_prim_time) {
|
||||
prim_time.resize(references.size());
|
||||
}
|
||||
else {
|
||||
prim_time.resize(0);
|
||||
}
|
||||
|
||||
/* build recursively */
|
||||
BVHNode *rootnode;
|
||||
|
@ -849,6 +860,9 @@ 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();
|
||||
if(need_prim_time) {
|
||||
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 +910,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 +933,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 +964,13 @@ 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);
|
||||
if(need_prim_time) {
|
||||
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 +983,9 @@ 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];
|
||||
if(need_prim_time) {
|
||||
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 +1052,17 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
|
|||
prim_type.reserve(reserve);
|
||||
prim_index.reserve(reserve);
|
||||
prim_object.reserve(reserve);
|
||||
if(need_prim_time) {
|
||||
prim_time.reserve(reserve);
|
||||
}
|
||||
}
|
||||
|
||||
prim_type.resize(range_end);
|
||||
prim_index.resize(range_end);
|
||||
prim_object.resize(range_end);
|
||||
if(need_prim_time) {
|
||||
prim_time.resize(range_end);
|
||||
}
|
||||
}
|
||||
spatial_spin_lock.unlock();
|
||||
|
||||
|
@ -1041,6 +1071,9 @@ 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);
|
||||
if(need_prim_time) {
|
||||
memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2)*num_new_leaf_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1053,6 +1086,9 @@ 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);
|
||||
if(need_prim_time) {
|
||||
memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2)*num_new_leaf_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,9 @@ protected:
|
|||
array<int>& prim_type;
|
||||
array<int>& prim_index;
|
||||
array<int>& prim_object;
|
||||
array<float2>& prim_time;
|
||||
|
||||
bool need_prim_time;
|
||||
|
||||
/* Build parameters. */
|
||||
BVHParams params;
|
||||
|
|
|
@ -104,6 +104,7 @@ public:
|
|||
primitive_mask = PRIMITIVE_ALL;
|
||||
|
||||
num_motion_curve_steps = 0;
|
||||
num_motion_triangle_steps = 0;
|
||||
}
|
||||
|
||||
/* SAH costs */
|
||||
|
|
|
@ -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 && kernel_data.bvh.use_bvh_steps) {
|
||||
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 && kernel_data.bvh.use_bvh_steps) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -1144,7 +1144,8 @@ typedef struct KernelBVH {
|
|||
int have_curves;
|
||||
int have_instancing;
|
||||
int use_qbvh;
|
||||
int pad1, pad2;
|
||||
int use_bvh_steps;
|
||||
int pad1;
|
||||
} KernelBVH;
|
||||
static_assert_align(KernelBVH, 16);
|
||||
|
||||
|
|
|
@ -144,7 +144,6 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y,
|
|||
case 86: r = kernel_tex_image_interp(__tex_image_byte4_086, x, y); break;
|
||||
case 87: r = kernel_tex_image_interp(__tex_image_byte4_087, x, y); break;
|
||||
case 88: r = kernel_tex_image_interp(__tex_image_byte4_088, x, y); break;
|
||||
case 89: r = kernel_tex_image_interp(__tex_image_byte4_089, x, y); break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
|
|
@ -1873,9 +1873,14 @@ 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;
|
||||
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
|
||||
}
|
||||
|
||||
void MeshManager::device_update_flags(Device * /*device*/,
|
||||
|
@ -2152,6 +2157,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 +2179,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();
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue