Cycles: Guiding cleaning up and refactoring the guiding code

In detail:
- Direct accesses of state attributes are replaced with the INTEGRATOR_STATE and INTEGRATOR_STATE_WRITE macros.
- Unified the checks for the __PATH_GUIDING define to use #  if defined (__PATH_GUIDING__).
- Even if __PATH_GUIDING__ is defined, we now check if the feature is enabled using if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {. This is important for later GPU ports.
- The kernel usage of the guiding field, surface, and volume sampling distributions is wrapped behind macros for each specific device (atm only CPU). This will make it easier for a GPU port later.
This commit is contained in:
Sebastian Herholz
2025-05-22 13:46:30 +02:00
parent 1d0c11987f
commit 5abf42012d
23 changed files with 177 additions and 140 deletions

View File

@@ -284,7 +284,7 @@ void CPUDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
void *CPUDevice::get_guiding_device() const
{
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
if (!guiding_device) {
if (guiding_device_type() == 8) {
guiding_device = make_unique<openpgl::cpp::Device>(PGL_DEVICE_TYPE_CPU_8);

View File

@@ -48,7 +48,7 @@ class CPUDevice : public Device {
RTCScene embree_scene = nullptr;
RTCDevice embree_device;
#endif
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
mutable unique_ptr<openpgl::cpp::Device> guiding_device;
#endif

View File

@@ -1335,7 +1335,7 @@ string PathTrace::full_report() const
void PathTrace::set_guiding_params(const GuidingParams &guiding_params, const bool reset)
{
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
if (guiding_params_.modified(guiding_params)) {
guiding_params_ = guiding_params;
@@ -1435,7 +1435,7 @@ void PathTrace::set_guiding_params(const GuidingParams &guiding_params, const bo
void PathTrace::guiding_prepare_structures()
{
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
const bool train = (guiding_params_.training_samples == 0) ||
(guiding_field_->GetIteration() < guiding_params_.training_samples);
@@ -1460,7 +1460,7 @@ void PathTrace::guiding_prepare_structures()
void PathTrace::guiding_update_structures()
{
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
VLOG_WORK << "Update path guiding structures";
VLOG_DEBUG << "Number of surface samples: " << guiding_sample_data_storage_->GetSizeSurface();

View File

@@ -289,7 +289,7 @@ class PathTrace {
/* Denoiser device descriptor which holds the denoised big tile for multi-device workloads. */
unique_ptr<PathTraceWork> big_tile_denoise_work_;
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
/* Guiding related attributes */
GuidingParams guiding_params_;

View File

@@ -143,7 +143,7 @@ class PathTraceWork {
return device_;
}
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
/* Initializes the per-thread guiding kernel data. */
virtual void guiding_init_kernel_globals(void * /*unused*/,
void * /*unused*/,

View File

@@ -148,7 +148,7 @@ void PathTraceWorkCPU::render_samples_full_pipeline(ThreadKernelGlobalsCPU *kern
}
}
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
if (kernel_globals->data.integrator.train_guiding) {
assert(kernel_globals->opgl_path_segment_storage);
assert(kernel_globals->opgl_path_segment_storage->GetNumSegments() == 0);
@@ -303,7 +303,7 @@ void PathTraceWorkCPU::cryptomatte_postproces()
});
}
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
/* NOTE: It seems that this is called before every rendering iteration/progression and not once per
* rendering. May be we find a way to call it only once per rendering. */
void PathTraceWorkCPU::guiding_init_kernel_globals(void *guiding_field,

View File

@@ -52,7 +52,7 @@ class PathTraceWorkCPU : public PathTraceWork {
int adaptive_sampling_converge_filter_count_active(const float threshold, bool reset) override;
void cryptomatte_postproces() override;
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
/* Initializes the per-thread guiding kernel data. The function sets the pointers to the
* global guiding field and the sample data storage as well es initializes the per-thread
* guided sampling distributions (e.g., SurfaceSamplingDistribution and

View File

@@ -388,6 +388,7 @@ set(SRC_UTIL_HEADERS
../util/atomic.h
../util/color.h
../util/defines.h
../util/guiding.h
../util/half.h
../util/hash.h
../util/math.h

View File

@@ -25,7 +25,7 @@ ThreadKernelGlobalsCPU::ThreadKernelGlobalsCPU(const KernelGlobalsCPU &kernel_gl
(void)osl_globals;
#endif
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
opgl_path_segment_storage = make_unique<openpgl::cpp::PathSegmentStorage>();
#endif
}

View File

@@ -74,7 +74,7 @@ struct ThreadKernelGlobalsCPU : public KernelGlobalsCPU {
OSLThreadData osl;
#endif
#ifdef __PATH_GUIDING__
#if defined(__PATH_GUIDING__)
/* Pointers to shared global data structures. */
openpgl::cpp::SampleStorage *opgl_sample_data_storage = nullptr;
openpgl::cpp::Field *opgl_guiding_field = nullptr;
@@ -95,5 +95,10 @@ using KernelGlobals = const ThreadKernelGlobalsCPU *;
#define kernel_data_fetch(name, index) (kg->name.fetch(index))
#define kernel_data_array(name) (kg->name.data)
#define kernel_data (kg->data)
#if defined(WITH_PATH_GUIDING)
# define guiding_guiding_field kg->opgl_guiding_field
# define guiding_ssd kg->opgl_surface_sampling_distribution
# define guiding_vsd kg->opgl_volume_sampling_distribution
#endif
CCL_NAMESPACE_END

View File

@@ -65,12 +65,12 @@ ccl_device_forceinline bool calculate_ris_target(ccl_private GuidingRISSample *r
#if defined(__PATH_GUIDING__)
static pgl_vec3f guiding_vec3f(const float3 v)
{
return openpgl::cpp::Vector3(v.x, v.y, v.z);
return {v.x, v.y, v.z};
}
static pgl_point3f guiding_point3f(const float3 v)
ccl_device_forceinline pgl_point3f guiding_point3f(const float3 v)
{
return openpgl::cpp::Point3(v.x, v.y, v.z);
return {v.x, v.y, v.z};
}
#endif
@@ -550,14 +550,11 @@ ccl_device_forceinline bool guiding_bsdf_init(KernelGlobals kg,
ccl_private float &rand)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (kg->opgl_surface_sampling_distribution->Init(
kg->opgl_guiding_field, guiding_point3f(P), rand))
{
kg->opgl_surface_sampling_distribution->ApplyCosineProduct(guiding_point3f(N));
if (guiding_ssd->Init(guiding_guiding_field, guiding_point3f(P), rand)) {
guiding_ssd->ApplyCosineProduct(guiding_point3f(N));
return true;
}
#endif
return false;
}
@@ -568,8 +565,8 @@ ccl_device_forceinline float guiding_bsdf_sample(KernelGlobals kg,
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
pgl_vec3f pgl_wo;
const pgl_point2f rand = openpgl::cpp::Point2(rand_bsdf.x, rand_bsdf.y);
const float pdf = kg->opgl_surface_sampling_distribution->SamplePDF(rand, pgl_wo);
const pgl_point2f rand = {rand_bsdf.x, rand_bsdf.y};
const float pdf = guiding_ssd->SamplePDF(rand, pgl_wo);
*wo = make_float3(pgl_wo.x, pgl_wo.y, pgl_wo.z);
return pdf;
#else
@@ -582,7 +579,7 @@ ccl_device_forceinline float guiding_bsdf_pdf(KernelGlobals kg,
const float3 wo)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
return kg->opgl_surface_sampling_distribution->PDF(guiding_vec3f(wo));
return guiding_ssd->PDF(guiding_vec3f(wo));
#else
return 0.0f;
#endif
@@ -593,7 +590,7 @@ ccl_device_forceinline float guiding_surface_incoming_radiance_pdf(KernelGlobals
const float3 wo)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
return kg->opgl_surface_sampling_distribution->IncomingRadiancePDF(guiding_vec3f(wo));
return guiding_ssd->IncomingRadiancePDF(guiding_vec3f(wo));
#else
return 0.0f;
#endif
@@ -614,11 +611,8 @@ ccl_device_forceinline bool guiding_phase_init(KernelGlobals kg,
return false;
}
if (kg->opgl_volume_sampling_distribution->Init(
kg->opgl_guiding_field, guiding_point3f(P), rand))
{
kg->opgl_volume_sampling_distribution->ApplySingleLobeHenyeyGreensteinProduct(guiding_vec3f(D),
g);
if (guiding_vsd->Init(guiding_guiding_field, guiding_point3f(P), rand)) {
guiding_vsd->ApplySingleLobeHenyeyGreensteinProduct(guiding_vec3f(D), g);
return true;
}
#endif
@@ -633,8 +627,8 @@ ccl_device_forceinline float guiding_phase_sample(KernelGlobals kg,
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
pgl_vec3f pgl_wo;
const pgl_point2f rand = openpgl::cpp::Point2(rand_phase.x, rand_phase.y);
const float pdf = kg->opgl_volume_sampling_distribution->SamplePDF(rand, pgl_wo);
const pgl_point2f rand = {rand_phase.x, rand_phase.y};
const float pdf = guiding_vsd->SamplePDF(rand, pgl_wo);
*wo = make_float3(pgl_wo.x, pgl_wo.y, pgl_wo.z);
return pdf;
#else
@@ -647,7 +641,7 @@ ccl_device_forceinline float guiding_phase_pdf(KernelGlobals kg,
const float3 wo)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
return kg->opgl_volume_sampling_distribution->PDF(guiding_vec3f(wo));
return guiding_vsd->PDF(guiding_vec3f(wo));
#else
return 0.0f;
#endif

View File

@@ -59,19 +59,19 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg,
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = FLT_MAX;
INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = 1.0f;
INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput;
#ifdef __PATH_GUIDING__
INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) = 1.0f;
INTEGRATOR_STATE_WRITE(state, guiding, path_segment) = nullptr;
INTEGRATOR_STATE_WRITE(state, guiding, use_surface_guiding) = false;
INTEGRATOR_STATE_WRITE(state, guiding, sample_surface_guiding_rand) = 0.5f;
INTEGRATOR_STATE_WRITE(state, guiding, surface_guiding_sampling_prob) = 0.0f;
INTEGRATOR_STATE_WRITE(state, guiding, bssrdf_sampling_prob) = 0.0f;
INTEGRATOR_STATE_WRITE(state, guiding, use_volume_guiding) = false;
INTEGRATOR_STATE_WRITE(state, guiding, sample_volume_guiding_rand) = 0.5f;
INTEGRATOR_STATE_WRITE(state, guiding, volume_guiding_sampling_prob) = 0.0f;
#if defined(__PATH_GUIDING__)
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) = 1.0f;
INTEGRATOR_STATE_WRITE(state, guiding, path_segment) = nullptr;
INTEGRATOR_STATE_WRITE(state, guiding, use_surface_guiding) = false;
INTEGRATOR_STATE_WRITE(state, guiding, sample_surface_guiding_rand) = 0.5f;
INTEGRATOR_STATE_WRITE(state, guiding, surface_guiding_sampling_prob) = 0.0f;
INTEGRATOR_STATE_WRITE(state, guiding, bssrdf_sampling_prob) = 0.0f;
INTEGRATOR_STATE_WRITE(state, guiding, use_volume_guiding) = false;
INTEGRATOR_STATE_WRITE(state, guiding, sample_volume_guiding_rand) = 0.5f;
INTEGRATOR_STATE_WRITE(state, guiding, volume_guiding_sampling_prob) = 0.0f;
}
#endif
#ifdef __MNEE__
INTEGRATOR_STATE_WRITE(state, path, mnee) = 0;
#endif
@@ -280,7 +280,9 @@ ccl_device_inline float path_state_continuation_probability(KernelGlobals kg,
* transform and do path termination a bit later on average. */
Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
throughput *= INTEGRATOR_STATE(state, path, unguided_throughput);
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
throughput *= INTEGRATOR_STATE(state, path, unguided_throughput);
}
#endif
return min(sqrtf(reduce_max(fabs(throughput))), 1.0f);
}

View File

@@ -211,7 +211,7 @@ ccl_device void shadow_linking_shade(KernelGlobals kg,
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag;
# ifdef __PATH_GUIDING__
# if defined(__PATH_GUIDING__)
if (kernel_data.integrator.train_guiding) {
guiding_record_light_surface_segment(kg, state, &isect);
INTEGRATOR_STATE(shadow_state, shadow_path, guiding_mis_weight) = mis_weight;

View File

@@ -271,11 +271,13 @@ integrate_direct_light_shadow_init_common(KernelGlobals kg,
/* Write Light-group, +1 as light-group is int but we need to encode into a uint8_t. */
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, lightgroup) = light_group + 1;
#ifdef __PATH_GUIDING__
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
state, guiding, path_segment);
INTEGRATOR_STATE(shadow_state, shadow_path, guiding_mis_weight) = 0.0f;
#if defined(__PATH_GUIDING__)
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
state, guiding, path_segment);
INTEGRATOR_STATE(shadow_state, shadow_path, guiding_mis_weight) = 0.0f;
}
#endif
return shadow_state;
@@ -473,7 +475,9 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
float mis_pdf = 1.0f;
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (kernel_data.integrator.use_surface_guiding) {
if (kernel_data.integrator.use_surface_guiding &&
(kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING))
{
label = surface_shader_bsdf_guided_sample_closure(kg,
state,
sd,
@@ -783,8 +787,10 @@ ccl_device int integrate_surface(KernelGlobals kg,
path_state_rng_load(state, &rng_state);
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
surface_shader_prepare_guiding(kg, state, &sd, &rng_state);
guiding_write_debug_passes(kg, state, &sd, render_buffer);
if (kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING) {
surface_shader_prepare_guiding(kg, state, &sd, &rng_state);
guiding_write_debug_passes(kg, state, &sd, render_buffer);
}
#endif
/* Direct light. */
PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT);

View File

@@ -39,7 +39,7 @@ struct VolumeIntegrateResult {
Spectrum direct_throughput;
float direct_t;
ShaderVolumePhases direct_phases;
# ifdef __PATH_GUIDING__
# if defined(__PATH_GUIDING__)
VolumeSampleMethod direct_sample_method;
# endif
@@ -591,7 +591,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
result.direct_t = volume_equiangular_sample(
ray, equiangular_coeffs, vstate.rscatter, &vstate.equiangular_pdf);
}
# ifdef __PATH_GUIDING__
# if defined(__PATH_GUIDING__)
result.direct_sample_method = vstate.direct_sample_method;
# endif
@@ -729,7 +729,7 @@ ccl_device_forceinline void integrate_volume_direct_light(
const ccl_private RNGState *ccl_restrict rng_state,
const float3 P,
const ccl_private ShaderVolumePhases *ccl_restrict phases,
# ifdef __PATH_GUIDING__
# if defined(__PATH_GUIDING__)
const ccl_private Spectrum unlit_throughput,
# endif
const ccl_private Spectrum throughput,
@@ -849,11 +849,13 @@ ccl_device_forceinline void integrate_volume_direct_light(
/* Write Light-group, +1 as light-group is int but we need to encode into a uint8_t. */
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, lightgroup) = ls.group + 1;
# ifdef __PATH_GUIDING__
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
state, guiding, path_segment);
INTEGRATOR_STATE(shadow_state, shadow_path, guiding_mis_weight) = 0.0f;
# if defined(__PATH_GUIDING__)
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
state, guiding, path_segment);
INTEGRATOR_STATE(shadow_state, shadow_path, guiding_mis_weight) = 0.0f;
}
# endif
integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
@@ -883,7 +885,9 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
int label;
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (kernel_data.integrator.use_guiding) {
if (kernel_data.integrator.use_guiding &&
(kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING))
{
label = volume_shader_phase_guided_sample(kg,
state,
sd,
@@ -1042,7 +1046,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
if (result.direct_scatter) {
const float3 direct_P = ray->P + result.direct_t * ray->D;
# ifdef __PATH_GUIDING__
# if defined(__PATH_GUIDING__)
if (kernel_data.integrator.use_guiding) {
# if PATH_GUIDING_LEVEL >= 1
if (result.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
@@ -1064,8 +1068,9 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
* unlit_throughput has to be adjusted to include the scattering at the previous segment.
*/
float3 scatterEval = one_float3();
if (state->guiding.path_segment) {
const pgl_vec3f scatteringWeight = state->guiding.path_segment->scatteringWeight;
if (INTEGRATOR_STATE(state, guiding, path_segment)) {
const pgl_vec3f scatteringWeight =
INTEGRATOR_STATE(state, guiding, path_segment)->scatteringWeight;
scatterEval = make_float3(scatteringWeight.x, scatteringWeight.y, scatteringWeight.z);
}
unlit_throughput /= scatterEval;
@@ -1075,8 +1080,10 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
}
# endif
# if PATH_GUIDING_LEVEL >= 4
volume_shader_prepare_guiding(
kg, state, &sd, rand_phase_guiding, direct_P, ray->D, &result.direct_phases);
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
volume_shader_prepare_guiding(
kg, state, &sd, rand_phase_guiding, direct_P, ray->D, &result.direct_phases);
}
# endif
}
# endif
@@ -1088,7 +1095,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
&rng_state,
direct_P,
&result.direct_phases,
# ifdef __PATH_GUIDING__
# if defined(__PATH_GUIDING__)
unlit_throughput,
# endif
result.direct_throughput,
@@ -1115,20 +1122,22 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
sd.P = ray->P + result.indirect_t * ray->D;
# if defined(__PATH_GUIDING__)
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
# if PATH_GUIDING_LEVEL >= 1
if (!guiding_generated_new_segment) {
guiding_record_volume_segment(kg, state, sd.P, sd.wi);
}
if (!guiding_generated_new_segment) {
guiding_record_volume_segment(kg, state, sd.P, sd.wi);
}
# endif
# if PATH_GUIDING_LEVEL >= 4
/* If the direct scatter event was generated using VOLUME_SAMPLE_EQUIANGULAR we need to
* initialize the guiding distribution at the indirect scatter position. */
if (result.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
rand_phase_guiding = path_state_rng_1D(kg, &rng_state, PRNG_VOLUME_PHASE_GUIDING_DISTANCE);
volume_shader_prepare_guiding(
kg, state, &sd, rand_phase_guiding, sd.P, ray->D, &result.indirect_phases);
}
/* If the direct scatter event was generated using VOLUME_SAMPLE_EQUIANGULAR we need to
* initialize the guiding distribution at the indirect scatter position. */
if (result.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
rand_phase_guiding = path_state_rng_1D(kg, &rng_state, PRNG_VOLUME_PHASE_GUIDING_DISTANCE);
volume_shader_prepare_guiding(
kg, state, &sd, rand_phase_guiding, sd.P, ray->D, &result.indirect_phases);
}
# endif
}
# endif
if (integrate_volume_phase_scatter(kg, state, &sd, ray, &rng_state, &result.indirect_phases)) {
@@ -1138,7 +1147,9 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
}
# if defined(__PATH_GUIDING__)
/* No guiding if we don't scatter. */
state->guiding.use_volume_guiding = false;
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
INTEGRATOR_STATE_WRITE(state, guiding, use_volume_guiding) = false;
}
# endif
return VOLUME_PATH_ATTENUATED;
}

View File

@@ -48,7 +48,7 @@ KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACIN
KERNEL_STRUCT_MEMBER(shadow_path, uint8_t, lightgroup, KERNEL_FEATURE_PATH_TRACING)
/* Path guiding. */
KERNEL_STRUCT_MEMBER(shadow_path, PackedSpectrum, unlit_throughput, KERNEL_FEATURE_PATH_GUIDING)
#ifdef __PATH_GUIDING__
#if defined(__PATH_GUIDING__)
KERNEL_STRUCT_MEMBER(shadow_path,
openpgl::cpp::PathSegment *,
path_segment,

View File

@@ -32,7 +32,7 @@
#include "util/types.h"
#ifdef __PATH_GUIDING__
#if defined(__PATH_GUIDING__)
# include "util/guiding.h" // IWYU pragma: keep
#endif

View File

@@ -81,8 +81,10 @@ ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init(
&kernel_integrator_state.next_shadow_path_index[0], 1);
atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1);
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel;
# ifdef __PATH_GUIDING__
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = nullptr;
# if defined(__PATH_GUIDING__)
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = nullptr;
}
# endif
return shadow_state;
}
@@ -203,8 +205,10 @@ ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init(
{
IntegratorShadowState shadow_state = (is_ao) ? &state->ao : &state->shadow;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel;
# ifdef __PATH_GUIDING__
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = nullptr;
# if defined(__PATH_GUIDING__)
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = nullptr;
}
# endif
return shadow_state;
}

View File

@@ -110,7 +110,7 @@ KERNEL_STRUCT_END_ARRAY(volume_stack,
/************************************ Path Guiding *****************************/
KERNEL_STRUCT_BEGIN(guiding)
#ifdef __PATH_GUIDING__
#if defined(__PATH_GUIDING__)
/* Current path segment of the random walk/path. */
KERNEL_STRUCT_MEMBER(guiding,
openpgl::cpp::PathSegment *,

View File

@@ -24,7 +24,7 @@ CCL_NAMESPACE_BEGIN
/* Guiding */
#ifdef __PATH_GUIDING__
#if defined(__PATH_GUIDING__)
ccl_device float surface_shader_average_sample_weight_squared_roughness(
const ccl_private ShaderData *sd)
@@ -54,7 +54,7 @@ ccl_device_inline void surface_shader_prepare_guiding(KernelGlobals kg,
{
/* Have any BSDF to guide? */
if (!(kernel_data.integrator.use_surface_guiding && (sd->flag & SD_BSDF_HAS_EVAL))) {
state->guiding.use_surface_guiding = false;
INTEGRATOR_STATE_WRITE(state, guiding, use_surface_guiding) = false;
return;
}
@@ -106,31 +106,35 @@ ccl_device_inline void surface_shader_prepare_guiding(KernelGlobals kg,
(diffuse_sampling_fraction <= 0.0f)) ||
!guiding_bsdf_init(kg, state, sd->P, sd->N, rand_bsdf_guiding))
{
state->guiding.use_surface_guiding = false;
state->guiding.surface_guiding_sampling_prob = 0.0f;
INTEGRATOR_STATE_WRITE(state, guiding, use_surface_guiding) = false;
INTEGRATOR_STATE_WRITE(state, guiding, surface_guiding_sampling_prob) = 0.f;
return;
}
state->guiding.use_surface_guiding = true;
INTEGRATOR_STATE_WRITE(state, guiding, use_surface_guiding) = true;
if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_PRODUCT_MIS)
{
state->guiding.surface_guiding_sampling_prob = surface_guiding_probability *
diffuse_sampling_fraction;
INTEGRATOR_STATE_WRITE(
state, guiding, surface_guiding_sampling_prob) = surface_guiding_probability *
diffuse_sampling_fraction;
}
else if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS)
{
state->guiding.surface_guiding_sampling_prob = surface_guiding_probability;
INTEGRATOR_STATE_WRITE(
state, guiding, surface_guiding_sampling_prob) = surface_guiding_probability;
}
else { // GUIDING_DIRECTIONAL_SAMPLING_TYPE_ROUGHNESS
state->guiding.surface_guiding_sampling_prob = surface_guiding_probability * avg_roughness;
INTEGRATOR_STATE_WRITE(
state, guiding, surface_guiding_sampling_prob) = surface_guiding_probability *
avg_roughness;
}
state->guiding.bssrdf_sampling_prob = bssrdf_sampling_fraction;
state->guiding.sample_surface_guiding_rand = rand_bsdf_guiding;
INTEGRATOR_STATE_WRITE(state, guiding, bssrdf_sampling_prob) = bssrdf_sampling_fraction;
INTEGRATOR_STATE_WRITE(state, guiding, sample_surface_guiding_rand) = rand_bsdf_guiding;
kernel_assert(state->guiding.surface_guiding_sampling_prob > 0.0f &&
state->guiding.surface_guiding_sampling_prob <= 1.0f);
kernel_assert(INTEGRATOR_STATE(state, guiding, surface_guiding_sampling_prob) > 0.0f &&
INTEGRATOR_STATE(state, guiding, surface_guiding_sampling_prob) <= 1.0f);
}
#endif
@@ -372,19 +376,22 @@ ccl_device_inline
}
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (pdf > 0.0f && state->guiding.use_surface_guiding) {
const float guiding_sampling_prob = state->guiding.surface_guiding_sampling_prob;
const float bssrdf_sampling_prob = state->guiding.bssrdf_sampling_prob;
const float guide_pdf = guiding_bsdf_pdf(kg, state, wo);
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
if (pdf > 0.0f && INTEGRATOR_STATE(state, guiding, use_surface_guiding)) {
const float guiding_sampling_prob = INTEGRATOR_STATE(
state, guiding, surface_guiding_sampling_prob);
const float bssrdf_sampling_prob = INTEGRATOR_STATE(state, guiding, bssrdf_sampling_prob);
const float guide_pdf = guiding_bsdf_pdf(kg, state, wo);
if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS)
{
pdf = (0.5f * guide_pdf * (1.0f - bssrdf_sampling_prob)) + 0.5f * pdf;
}
else {
pdf = (guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob)) +
(1.0f - guiding_sampling_prob) * pdf;
if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS)
{
pdf = (0.5f * guide_pdf * (1.0f - bssrdf_sampling_prob)) + 0.5f * pdf;
}
else {
pdf = (guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob)) +
(1.0f - guiding_sampling_prob) * pdf;
}
}
}
#endif
@@ -457,7 +464,7 @@ surface_shader_bssrdf_sample_weight(const ccl_private ShaderData *ccl_restrict s
return weight;
}
#ifdef __PATH_GUIDING__
#if defined(__PATH_GUIDING__)
/* Sample direction for picked BSDF, and return evaluation and pdf for all
* BSDFs combined using MIS. */
@@ -476,13 +483,14 @@ ccl_device int surface_shader_bsdf_guided_sample_closure_mis(KernelGlobals kg,
/* BSSRDF should already have been handled elsewhere. */
kernel_assert(CLOSURE_IS_BSDF(sc->type));
const bool use_surface_guiding = state->guiding.use_surface_guiding;
const float guiding_sampling_prob = state->guiding.surface_guiding_sampling_prob;
const float bssrdf_sampling_prob = state->guiding.bssrdf_sampling_prob;
const bool use_surface_guiding = INTEGRATOR_STATE(state, guiding, use_surface_guiding);
const float guiding_sampling_prob = INTEGRATOR_STATE(
state, guiding, surface_guiding_sampling_prob);
const float bssrdf_sampling_prob = INTEGRATOR_STATE(state, guiding, bssrdf_sampling_prob);
/* Decide between sampling guiding distribution and BSDF. */
bool sample_guiding = false;
float rand_bsdf_guiding = state->guiding.sample_surface_guiding_rand;
float rand_bsdf_guiding = INTEGRATOR_STATE(state, guiding, sample_surface_guiding_rand);
if (use_surface_guiding && rand_bsdf_guiding < guiding_sampling_prob) {
sample_guiding = true;
@@ -609,12 +617,13 @@ ccl_device int surface_shader_bsdf_guided_sample_closure_ris(KernelGlobals kg,
/* BSSRDF should already have been handled elsewhere. */
kernel_assert(CLOSURE_IS_BSDF(sc->type));
const bool use_surface_guiding = state->guiding.use_surface_guiding;
const float guiding_sampling_prob = state->guiding.surface_guiding_sampling_prob;
const float bssrdf_sampling_prob = state->guiding.bssrdf_sampling_prob;
const bool use_surface_guiding = INTEGRATOR_STATE(state, guiding, use_surface_guiding);
const float guiding_sampling_prob = INTEGRATOR_STATE(
state, guiding, surface_guiding_sampling_prob);
const float bssrdf_sampling_prob = INTEGRATOR_STATE(state, guiding, bssrdf_sampling_prob);
/* Decide between sampling guiding distribution and BSDF. */
const float rand_bsdf_guiding = state->guiding.sample_surface_guiding_rand;
const float rand_bsdf_guiding = INTEGRATOR_STATE(state, guiding, sample_surface_guiding_rand);
/* Initialize to zero. */
int label = LABEL_NONE;

View File

@@ -82,7 +82,7 @@ ccl_device_inline void volume_shader_copy_phases(ccl_private ShaderVolumePhases
/* Guiding */
# ifdef __PATH_GUIDING__
# if defined(__PATH_GUIDING__)
ccl_device_inline void volume_shader_prepare_guiding(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
@@ -94,7 +94,7 @@ ccl_device_inline void volume_shader_prepare_guiding(KernelGlobals kg,
/* Have any phase functions to guide? */
const int num_phases = phases->num_closure;
if (!kernel_data.integrator.use_volume_guiding || num_phases == 0) {
state->guiding.use_volume_guiding = false;
INTEGRATOR_STATE_WRITE(state, guiding, use_volume_guiding) = false;
return;
}
@@ -139,16 +139,17 @@ ccl_device_inline void volume_shader_prepare_guiding(KernelGlobals kg,
const ccl_private ShaderVolumeClosure *svc = &phases->closure[phase_id];
const float phase_g = volume_phase_get_g(svc);
if (!guiding_phase_init(kg, state, P, D, phase_g, rand_phase_guiding)) {
state->guiding.use_volume_guiding = false;
INTEGRATOR_STATE_WRITE(state, guiding, use_volume_guiding) = false;
return;
}
state->guiding.use_volume_guiding = true;
state->guiding.sample_volume_guiding_rand = rand_phase_guiding;
state->guiding.volume_guiding_sampling_prob = volume_guiding_probability * phase_weight;
INTEGRATOR_STATE_WRITE(state, guiding, use_volume_guiding) = true;
INTEGRATOR_STATE_WRITE(state, guiding, sample_volume_guiding_rand) = rand_phase_guiding;
INTEGRATOR_STATE_WRITE(
state, guiding, volume_guiding_sampling_prob) = volume_guiding_probability * phase_weight;
kernel_assert(state->guiding.volume_guiding_sampling_prob > 0.0f &&
state->guiding.volume_guiding_sampling_prob <= 1.0f);
kernel_assert(INTEGRATOR_STATE(state, guiding, volume_guiding_sampling_prob) > 0.0f &&
INTEGRATOR_STATE(state, guiding, volume_guiding_sampling_prob) <= 1.0f);
}
# endif
@@ -239,10 +240,13 @@ ccl_device float volume_shader_phase_eval(KernelGlobals kg,
float pdf = _volume_shader_phase_eval_mis(sd, phases, wo, phase_eval, 0.0f, 0.0f);
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (state->guiding.use_volume_guiding) {
const float guiding_sampling_prob = state->guiding.volume_guiding_sampling_prob;
const float guide_pdf = guiding_phase_pdf(kg, state, wo);
pdf = (guiding_sampling_prob * guide_pdf) + (1.0f - guiding_sampling_prob) * pdf;
if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
if (INTEGRATOR_STATE(state, guiding, use_volume_guiding)) {
const float guiding_sampling_prob = INTEGRATOR_STATE(
state, guiding, volume_guiding_sampling_prob);
const float guide_pdf = guiding_phase_pdf(kg, state, wo);
pdf = (guiding_sampling_prob * guide_pdf) + (1.0f - guiding_sampling_prob) * pdf;
}
}
# endif
@@ -255,7 +259,7 @@ ccl_device float volume_shader_phase_eval(KernelGlobals kg,
return pdf;
}
# ifdef __PATH_GUIDING__
# if defined(__PATH_GUIDING__)
ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
IntegratorState state,
const ccl_private ShaderData *sd,
@@ -267,11 +271,12 @@ ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
ccl_private float *unguided_phase_pdf,
ccl_private float *sampled_roughness)
{
const bool use_volume_guiding = state->guiding.use_volume_guiding;
const float guiding_sampling_prob = state->guiding.volume_guiding_sampling_prob;
const bool use_volume_guiding = INTEGRATOR_STATE(state, guiding, use_volume_guiding);
const float guiding_sampling_prob = INTEGRATOR_STATE(
state, guiding, volume_guiding_sampling_prob);
/* Decide between sampling guiding distribution and phase. */
float rand_phase_guiding = state->guiding.sample_volume_guiding_rand;
float rand_phase_guiding = INTEGRATOR_STATE(state, guiding, sample_volume_guiding_rand);
bool sample_guiding = false;
if (use_volume_guiding && rand_phase_guiding < guiding_sampling_prob) {
sample_guiding = true;

View File

@@ -201,7 +201,7 @@ CCL_NAMESPACE_BEGIN
# endif
#endif
#ifndef __KERNEL_GPU__
# ifdef WITH_PATH_GUIDING
# if defined(WITH_PATH_GUIDING)
# define __PATH_GUIDING__
# endif
# define __VOLUME_RECORD_ALL__

View File

@@ -4,7 +4,7 @@
#pragma once
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
# include <openpgl/cpp/OpenPGL.h> // IWYU pragma: export
# include <openpgl/version.h> // IWYU pragma: export
#endif
@@ -15,7 +15,7 @@ CCL_NAMESPACE_BEGIN
static int guiding_device_type()
{
#ifdef WITH_PATH_GUIDING
#if defined(WITH_PATH_GUIDING)
# if defined(__ARM_NEON)
return 8;
# else