From 2c5531c0a521119a2f5c88b4ba2a67234c537d2b Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 7 Nov 2018 12:58:12 +0100 Subject: Cycles: Added Embree as BVH option for CPU renders. Note that this is turned off by default and must be enabled at build time with the CMake WITH_CYCLES_EMBREE flag. Embree must be built as a static library with ray masking turned on, the `make deps` scripts have been updated accordingly. There, Embree is off by default too and must be enabled with the WITH_EMBREE flag. Using Embree allows for much faster rendering of deformation motion blur while reducing the memory footprint. TODO: GPU implementation, deduplication of data, leveraging more of Embrees features (e.g. tessellation cache). Differential Revision: https://developer.blender.org/D3682 --- intern/cycles/kernel/CMakeLists.txt | 1 + intern/cycles/kernel/bvh/bvh.h | 99 ++++++++++++++++++ intern/cycles/kernel/bvh/bvh_embree.h | 126 +++++++++++++++++++++++ intern/cycles/kernel/geom/geom_curve_intersect.h | 24 +++-- intern/cycles/kernel/geom/geom_object.h | 6 ++ intern/cycles/kernel/kernel_types.h | 21 +++- 6 files changed, 267 insertions(+), 10 deletions(-) create mode 100644 intern/cycles/kernel/bvh/bvh_embree.h (limited to 'intern/cycles/kernel') diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 08efede36df..92cb66bdec9 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC_BVH_HEADERS bvh/obvh_traversal.h bvh/obvh_volume.h bvh/obvh_volume_all.h + bvh/bvh_embree.h ) set(SRC_HEADERS diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index d11d999134c..0a5998a6544 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -25,6 +25,10 @@ * the code has been extended and modified to support more primitives and work * with CPU/CUDA/OpenCL. */ +#ifdef __EMBREE__ +#include "kernel/bvh/bvh_embree.h" +#endif + CCL_NAMESPACE_BEGIN #include "kernel/bvh/bvh_types.h" @@ -185,6 +189,21 @@ ccl_device_intersect bool scene_intersect(KernelGlobals *kg, if (!scene_intersect_valid(&ray)) { return false; } +#ifdef __EMBREE__ + if(kernel_data.bvh.scene) { + isect->t = ray.t; + CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_REGULAR); + IntersectContext rtc_ctx(&ctx); + RTCRayHit ray_hit; + kernel_embree_setup_rayhit(ray, ray_hit, visibility); + rtcIntersect1(kernel_data.bvh.scene, &rtc_ctx.context, &ray_hit); + if(ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID && ray_hit.hit.primID != RTC_INVALID_GEOMETRY_ID) { + kernel_embree_convert_hit(kg, &ray_hit.ray, &ray_hit.hit, isect); + return true; + } + return false; + } +#endif /* __EMBREE__ */ #ifdef __OBJECT_MOTION__ if(kernel_data.bvh.have_motion) { # ifdef __HAIR__ @@ -232,6 +251,55 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, if (!scene_intersect_valid(&ray)) { return false; } +#ifdef __EMBREE__ + if(kernel_data.bvh.scene) { + CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SSS); + ctx.lcg_state = lcg_state; + ctx.max_hits = max_hits; + ctx.ss_isect = local_isect; + local_isect->num_hits = 0; + ctx.sss_object_id = local_object; + IntersectContext rtc_ctx(&ctx); + RTCRay rtc_ray; + kernel_embree_setup_ray(ray, rtc_ray, PATH_RAY_ALL_VISIBILITY); + + /* Get the Embree scene for this intersection. */ + RTCGeometry geom = rtcGetGeometry(kernel_data.bvh.scene, local_object * 2); + if(geom) { + Transform ob_itfm; + float3 P = ray.P; + float3 dir = ray.D; + float3 idir = ray.D; + const int object_flag = kernel_tex_fetch(__object_flag, local_object); + if(!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + Transform ob_itfm; + rtc_ray.tfar = bvh_instance_motion_push(kg, + local_object, + &ray, + &P, + &dir, + &idir, + ray.t, + &ob_itfm); + /* bvh_instance_motion_push() returns the inverse transform but it's not needed here. */ + (void)ob_itfm; + + rtc_ray.org_x = P.x; + rtc_ray.org_y = P.y; + rtc_ray.org_z = P.z; + rtc_ray.dir_x = dir.x; + rtc_ray.dir_y = dir.y; + rtc_ray.dir_z = dir.z; + } + RTCScene scene = (RTCScene)rtcGetGeometryUserData(geom); + if(scene) { + rtcOccluded1(scene, &rtc_ctx.context, &rtc_ray); + } + } + + return local_isect->num_hits > 0; + } +#endif /* __EMBREE__ */ #ifdef __OBJECT_MOTION__ if(kernel_data.bvh.have_motion) { return bvh_intersect_local_motion(kg, @@ -262,6 +330,24 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg, if (!scene_intersect_valid(ray)) { return false; } +# ifdef __EMBREE__ + if(kernel_data.bvh.scene) { + CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SHADOW_ALL); + ctx.isect_s = isect; + ctx.max_hits = max_hits; + ctx.num_hits = 0; + IntersectContext rtc_ctx(&ctx); + RTCRay rtc_ray; + kernel_embree_setup_ray(*ray, rtc_ray, PATH_RAY_SHADOW); + rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); + + if(ctx.num_hits > max_hits) { + return true; + } + *num_hits = ctx.num_hits; + return rtc_ray.tfar == -INFINITY; + } +# endif # ifdef __OBJECT_MOTION__ if(kernel_data.bvh.have_motion) { # ifdef __HAIR__ @@ -355,6 +441,19 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg, if (!scene_intersect_valid(ray)) { return false; } +# ifdef __EMBREE__ + if(kernel_data.bvh.scene) { + CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_VOLUME_ALL); + ctx.isect_s = isect; + ctx.max_hits = max_hits; + ctx.num_hits = 0; + IntersectContext rtc_ctx(&ctx); + RTCRay rtc_ray; + kernel_embree_setup_ray(*ray, rtc_ray, visibility); + rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); + return rtc_ray.tfar == -INFINITY; + } +# endif # ifdef __OBJECT_MOTION__ if(kernel_data.bvh.have_motion) { return bvh_intersect_volume_all_motion(kg, ray, isect, max_hits, visibility); diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h new file mode 100644 index 00000000000..34a099ebb4d --- /dev/null +++ b/intern/cycles/kernel/bvh/bvh_embree.h @@ -0,0 +1,126 @@ +/* + * Copyright 2018, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "kernel/kernel_compat_cpu.h" +#include "kernel/split/kernel_split_data_types.h" +#include "kernel/kernel_globals.h" +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +struct CCLIntersectContext { + typedef enum { + RAY_REGULAR = 0, + RAY_SHADOW_ALL = 1, + RAY_SSS = 2, + RAY_VOLUME_ALL = 3, + + } RayType; + + KernelGlobals *kg; + RayType type; + + /* for shadow rays */ + Intersection *isect_s; + int max_hits; + int num_hits; + + /* for SSS Rays: */ + LocalIntersection *ss_isect; + int sss_object_id; + uint *lcg_state; + + CCLIntersectContext(KernelGlobals *kg_, RayType type_) + { + kg = kg_; + type = type_; + max_hits = 1; + num_hits = 0; + isect_s = NULL; + ss_isect = NULL; + sss_object_id = -1; + lcg_state = NULL; + } +}; + +class IntersectContext +{ +public: + IntersectContext(CCLIntersectContext* ctx) + { + rtcInitIntersectContext(&context); + userRayExt = ctx; + } + RTCIntersectContext context; + CCLIntersectContext* userRayExt; +}; + +ccl_device_inline void kernel_embree_setup_ray(const Ray& ray, RTCRay& rtc_ray, const uint visibility) +{ + rtc_ray.org_x = ray.P.x; + rtc_ray.org_y = ray.P.y; + rtc_ray.org_z = ray.P.z; + rtc_ray.dir_x = ray.D.x; + rtc_ray.dir_y = ray.D.y; + rtc_ray.dir_z = ray.D.z; + rtc_ray.tnear = 0.0f; + rtc_ray.tfar = ray.t; + rtc_ray.time = ray.time; + rtc_ray.mask = visibility; +} + +ccl_device_inline void kernel_embree_setup_rayhit(const Ray& ray, RTCRayHit& rayhit, const uint visibility) +{ + kernel_embree_setup_ray(ray, rayhit.ray, visibility); + rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; + rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID; +} + +ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect) +{ + bool is_hair = hit->geomID & 1; + isect->u = is_hair ? hit->u : 1.0f - hit->v - hit->u; + isect->v = is_hair ? hit->v : hit->u; + isect->t = ray->tfar; + isect->Ng = make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z); + if(hit->instID[0] != RTC_INVALID_GEOMETRY_ID) { + RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); + isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) + kernel_tex_fetch(__object_node, hit->instID[0]/2); + isect->object = hit->instID[0]/2; + } + else { + isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(kernel_data.bvh.scene, hit->geomID)); + isect->object = OBJECT_NONE; + } + isect->type = kernel_tex_fetch(__prim_type, isect->prim); +} + +ccl_device_inline void kernel_embree_convert_local_hit(KernelGlobals *kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect, int local_object_id) +{ + isect->u = 1.0f - hit->v - hit->u; + isect->v = hit->u; + isect->t = ray->tfar; + isect->Ng = make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z); + RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2)); + isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) + kernel_tex_fetch(__object_node, local_object_id); + isect->object = local_object_id; + isect->type = kernel_tex_fetch(__prim_type, isect->prim); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index 4cfbe21685c..5901429918c 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -817,16 +817,24 @@ ccl_device_inline float3 curve_refine(KernelGlobals *kg, sd->Ng = normalize(-(D - tg * (dot(tg, D)))); } else { - /* direction from inside to surface of curve */ - float3 p_curr = curvepoint(isect->u, p[0], p[1], p[2], p[3]); - sd->Ng = normalize(P - p_curr); +#ifdef __EMBREE__ + if(kernel_data.bvh.scene) { + sd->Ng = normalize(isect->Ng); + } + else +#endif + { + /* direction from inside to surface of curve */ + float3 p_curr = curvepoint(isect->u, p[0], p[1], p[2], p[3]); + sd->Ng = normalize(P - p_curr); - /* adjustment for changing radius */ - float gd = isect->v; + /* adjustment for changing radius */ + float gd = isect->v; - if(gd != 0.0f) { - sd->Ng = sd->Ng - gd * tg; - sd->Ng = normalize(sd->Ng); + if(gd != 0.0f) { + sd->Ng = sd->Ng - gd * tg; + sd->Ng = normalize(sd->Ng); + } } } diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 0eb8ce2cf8b..669c932d720 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -78,6 +78,12 @@ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, int const uint num_steps = kernel_tex_fetch(__objects, object).numsteps * 2 + 1; Transform tfm; +#ifdef __EMBREE__ + if(kernel_data.bvh.scene) { + transform_motion_array_interpolate_straight(&tfm, motion, num_steps, time); + } + else +#endif transform_motion_array_interpolate(&tfm, motion, num_steps, time); return tfm; diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 230e90c8b31..a30169cc231 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -25,6 +25,11 @@ # define __KERNEL_CPU__ #endif +#if defined(__KERNEL_CPU__) && defined(WITH_EMBREE) +#include +#include +#endif + /* TODO(sergey): This is only to make it possible to include this header * from outside of the kernel. but this could be done somewhat cleaner? */ @@ -97,6 +102,9 @@ CCL_NAMESPACE_BEGIN # define __SHADOW_RECORD_ALL__ # define __VOLUME_DECOUPLED__ # define __VOLUME_RECORD_ALL__ +# ifdef WITH_EMBREE +# define __EMBREE__ +# endif #endif /* __KERNEL_CPU__ */ #ifdef __KERNEL_CUDA__ @@ -722,6 +730,9 @@ typedef struct Ray { /* Intersection */ typedef struct Intersection { +#ifdef __EMBREE__ + float3 Ng; +#endif float t, u, v; int prim; int object; @@ -1396,7 +1407,7 @@ typedef enum KernelBVHLayout { BVH_LAYOUT_BVH2 = (1 << 0), BVH_LAYOUT_BVH4 = (1 << 1), BVH_LAYOUT_BVH8 = (1 << 2), - + BVH_LAYOUT_EMBREE = (1 << 3), BVH_LAYOUT_DEFAULT = BVH_LAYOUT_BVH8, BVH_LAYOUT_ALL = (unsigned int)(-1), } KernelBVHLayout; @@ -1409,7 +1420,13 @@ typedef struct KernelBVH { int have_instancing; int bvh_layout; int use_bvh_steps; - int pad1, pad2; + int pad1; +#ifdef __EMBREE__ + RTCScene scene; +#else + void *unused; +#endif + int pad2, pad3; } KernelBVH; static_assert_align(KernelBVH, 16); -- cgit v1.2.3