From b5ca99a10ce73ae82ffc72e3d1dcc0f65acf3ea9 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 8 May 2023 14:43:37 +0200 Subject: [PATCH 1/2] Shadow linking: Select one of dedicated lights Before this change the shadow ray for shadow linking was picking the closest light. Now a random light on the ray is chosen. This is more noisy than integrating all lights in the direction of the main path, but this is the only way to properly support complex scenarios where there are multiple blockers between the shading point and the light sources, with different linked settings. Another unfortunate aspect of this change is increased state size. This is because the light sample needs to be scaled, so some extra information is needed. From the intersection kernel it is not yet possible to write to the shadow path state as this intersection kernel is marked as not generating shadow rays for the performance reasons. Perhaps proper solution would be to run profiler to see if the current dedicated light intersection + shading kernel split actually gives speedup (by allowing the shadow and main path to run in parallel as much as possible) or if the speedup is neglectable and it is better to save memory of the state. Ref #104972 --- .../integrator/intersect_dedicated_light.h | 18 ++- .../kernel/integrator/shade_dedicated_light.h | 3 +- .../cycles/kernel/integrator/state_template.h | 1 + intern/cycles/kernel/light/light.h | 111 ++++++++++++------ 4 files changed, 93 insertions(+), 40 deletions(-) diff --git a/intern/cycles/kernel/integrator/intersect_dedicated_light.h b/intern/cycles/kernel/integrator/intersect_dedicated_light.h index faad73c1dae..01a83a0c1c4 100644 --- a/intern/cycles/kernel/integrator/intersect_dedicated_light.h +++ b/intern/cycles/kernel/integrator/intersect_dedicated_light.h @@ -8,6 +8,7 @@ #include "kernel/integrator/shade_surface.h" #include "kernel/integrator/shadow_linking.h" #include "kernel/light/light.h" +#include "kernel/sample/lcg.h" CCL_NAMESPACE_BEGIN @@ -28,6 +29,11 @@ ccl_device bool shadow_linking_pick_light_intersection(KernelGlobals kg, const int last_object = INTEGRATOR_STATE(state, isect, object); const int last_type = INTEGRATOR_STATE(state, isect, type); + uint lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_hash), + INTEGRATOR_STATE(state, path, rng_offset), + INTEGRATOR_STATE(state, path, sample), + 0x68bc21eb); + /* The lights_intersect() has a "refining" behavior: it chooses intersection closer to the * current intersection's distance. Hence initialize the fields which are accessed prior to * recording an intersection. */ @@ -36,19 +42,19 @@ ccl_device bool shadow_linking_pick_light_intersection(KernelGlobals kg, // TODO: Support mesh emitters. - // TODO: Support multiple light sources. - // TODO: Distant lights. // TODO: Only if ray is not fully occluded. - // TODO: What of the actual shadow ray hits the same light through a semi-transparent surface? + const int num_hits = lights_intersect_shadow_linked( + kg, ray, isect, last_prim, last_object, last_type, path_flag, &lcg_state); - if (!lights_intersect_shadow_linked( - kg, ray, isect, last_prim, last_object, last_type, path_flag)) { + if (num_hits == 0) { return false; } + INTEGRATOR_STATE_WRITE(state, shadow_link, dedicated_light_weight) = num_hits; + return true; } @@ -79,7 +85,7 @@ ccl_device bool shadow_linking_intersect(KernelGlobals kg, IntegratorState state shadow_linking_store_last_primitives(state); /* Write intersection result into global integrator state memory, so that the - * shade_dedicated_light kernel can use it for calculation of the light sample, */ + * shade_dedicated_light kernel can use it for calculation of the light sample. */ integrator_state_write_isect(state, &isect); integrator_path_next(kg, diff --git a/intern/cycles/kernel/integrator/shade_dedicated_light.h b/intern/cycles/kernel/integrator/shade_dedicated_light.h index e3044845dd9..31180e3038b 100644 --- a/intern/cycles/kernel/integrator/shade_dedicated_light.h +++ b/intern/cycles/kernel/integrator/shade_dedicated_light.h @@ -73,7 +73,8 @@ ccl_device void shadow_linking_shade(KernelGlobals kg, IntegratorState state) mis_weight = light_sample_mis_weight_forward_lamp(kg, state, path_flag, &ls, ray.P); } - const Spectrum bsdf_spectrum = light_eval * mis_weight; + const Spectrum bsdf_spectrum = light_eval * mis_weight * + INTEGRATOR_STATE(state, shadow_link, dedicated_light_weight); shadow_linking_setup_ray_from_intersection(state, &ray, &isect); diff --git a/intern/cycles/kernel/integrator/state_template.h b/intern/cycles/kernel/integrator/state_template.h index dab68df6644..2b603ee8e8b 100644 --- a/intern/cycles/kernel/integrator/state_template.h +++ b/intern/cycles/kernel/integrator/state_template.h @@ -139,6 +139,7 @@ KERNEL_STRUCT_END(guiding) /******************************* Shadow linking *******************************/ KERNEL_STRUCT_BEGIN(shadow_link) +KERNEL_STRUCT_MEMBER(shadow_link, float, dedicated_light_weight, KERNEL_FEATURE_SHADOW_LINKING) /* Copy of primitive and object from the last main path intersection. */ KERNEL_STRUCT_MEMBER(shadow_link, int, last_isect_prim, KERNEL_FEATURE_SHADOW_LINKING) KERNEL_STRUCT_MEMBER(shadow_link, int, last_isect_object, KERNEL_FEATURE_SHADOW_LINKING) diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index 84de198a58c..73722b34280 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -9,6 +9,7 @@ #include "kernel/light/point.h" #include "kernel/light/spot.h" #include "kernel/light/triangle.h" +#include "kernel/sample/lcg.h" #include "kernel/sample/mapping.h" @@ -245,16 +246,23 @@ ccl_device_noinline bool light_sample(KernelGlobals kg, /* Intersect ray with individual light. */ template -ccl_device bool lights_intersect_impl(KernelGlobals kg, - ccl_private const Ray *ccl_restrict ray, - ccl_private Intersection *ccl_restrict isect, - const int last_prim, - const int last_object, - const int last_type, - const uint32_t path_flag, - const uint8_t path_mnee, - const int receiver_forward) +ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg, + ccl_private const Ray *ccl_restrict ray, + ccl_private Intersection *ccl_restrict isect, + const int last_prim, + const int last_object, + const int last_type, + const uint32_t path_flag, + const uint8_t path_mnee, + const int receiver_forward, + ccl_private uint *lcg_state) { +#ifdef __SHADOW_LINKING__ + const bool is_indirect_ray = !(path_flag & PATH_RAY_CAMERA); +#endif + + int num_hits = 0; + for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) { const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); @@ -289,8 +297,7 @@ ccl_device bool lights_intersect_impl(KernelGlobals kg, * For the shadow path used for the dedicated light shading ignore all non-shadow-linked * lights. */ if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) { - if (is_main_path) { - const bool is_indirect_ray = !(path_flag & PATH_RAY_CAMERA); + if constexpr (is_main_path) { if (is_indirect_ray && kernel_data_fetch(lights, lamp).shadow_set_membership) { continue; } @@ -299,7 +306,6 @@ ccl_device bool lights_intersect_impl(KernelGlobals kg, continue; } } - #endif #ifdef __LIGHT_LINKING__ @@ -331,19 +337,39 @@ ccl_device bool lights_intersect_impl(KernelGlobals kg, continue; } - if (t < isect->t && - !(last_prim == lamp && last_object == OBJECT_NONE && last_type == PRIMITIVE_LAMP)) - { - isect->t = t; - isect->u = u; - isect->v = v; - isect->type = PRIMITIVE_LAMP; - isect->prim = lamp; - isect->object = OBJECT_NONE; + /* Avoid self-intersections. */ + if (last_prim == lamp && last_object == OBJECT_NONE && last_type == PRIMITIVE_LAMP) { + continue; } + + ++num_hits; + +#ifdef __SHADOW_LINKING__ + if constexpr (!is_main_path) { + /* The non-main rays are only raced by the dedicated light kernel, after the shadow linking + * feature check. */ + kernel_assert(kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING); + + if ((isect->prim != PRIM_NONE) && (lcg_step_float(lcg_state) < 1.0f / num_hits)) { + continue; + } + } + else +#endif + if (t >= isect->t) + { + continue; + } + + isect->t = t; + isect->u = u; + isect->v = v; + isect->type = PRIMITIVE_LAMP; + isect->prim = lamp; + isect->object = OBJECT_NONE; } - return isect->prim != PRIM_NONE; + return num_hits; } ccl_device bool lights_intersect(KernelGlobals kg, @@ -358,20 +384,39 @@ ccl_device bool lights_intersect(KernelGlobals kg, const uint8_t path_mnee = INTEGRATOR_STATE(state, path, mnee); const int receiver_forward = light_link_receiver_forward(kg, state); - return lights_intersect_impl( - kg, ray, isect, last_prim, last_object, last_type, path_flag, path_mnee, receiver_forward); + lights_intersect_impl(kg, + ray, + isect, + last_prim, + last_object, + last_type, + path_flag, + path_mnee, + receiver_forward, + nullptr); + + return isect->prim != PRIM_NONE; } -ccl_device bool lights_intersect_shadow_linked(KernelGlobals kg, - ccl_private const Ray *ccl_restrict ray, - ccl_private Intersection *ccl_restrict isect, - const int last_prim, - const int last_object, - const int last_type, - const uint32_t path_flag) +ccl_device int lights_intersect_shadow_linked(KernelGlobals kg, + ccl_private const Ray *ccl_restrict ray, + ccl_private Intersection *ccl_restrict isect, + const int last_prim, + const int last_object, + const int last_type, + const uint32_t path_flag, + ccl_private uint *lcg_state) { - return lights_intersect_impl( - kg, ray, isect, last_prim, last_object, last_type, path_flag, PATH_MNEE_NONE, OBJECT_NONE); + return lights_intersect_impl(kg, + ray, + isect, + last_prim, + last_object, + last_type, + path_flag, + PATH_MNEE_NONE, + OBJECT_NONE, + lcg_state); } /* Setup light sample from intersection. */ -- 2.30.2 From 7233cf42ac65e2ea980474faf9ea6afdf27e3c24 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 16 May 2023 16:13:17 +0200 Subject: [PATCH 2/2] Remove constexpr from the if statement Is not supported with CUDA 10. --- intern/cycles/kernel/light/light.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index 24a3486b91a..713e6a97570 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -297,7 +297,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg, * For the shadow path used for the dedicated light shading ignore all non-shadow-linked * lights. */ if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) { - if constexpr (is_main_path) { + if (is_main_path) { if (is_indirect_ray && kernel_data_fetch(lights, lamp).shadow_set_membership) { continue; } @@ -345,7 +345,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg, ++num_hits; #ifdef __SHADOW_LINKING__ - if constexpr (!is_main_path) { + if (!is_main_path) { /* The non-main rays are only raced by the dedicated light kernel, after the shadow linking * feature check. */ kernel_assert(kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING); -- 2.30.2