size_t generate_environment_sample(
        SamplingContext&            sampling_context,
        const EnvironmentEDF*       env_edf,
        SampleVector&               samples)
    {
        // Sample the environment.
        sampling_context.split_in_place(2, 1);
        InputEvaluator input_evaluator(m_texture_cache);
        Vector3d outgoing;
        Spectrum env_edf_value;
        double env_edf_prob;
        env_edf->sample(
            m_shading_context,
            input_evaluator,
            sampling_context.next_vector2<2>(),
            outgoing,               // points toward the environment
            env_edf_value,
            env_edf_prob);

        // Uniformly sample the tangent disk.
        sampling_context.split_in_place(2, 1);
        const Vector2d p =
            m_scene_radius
            * sample_disk_uniform(sampling_context.next_vector2<2>());

        // Compute the origin of the light ray.
        const Basis3d basis(-outgoing);
        const Vector3d ray_origin =
            m_scene_center
            - m_safe_scene_diameter * basis.get_normal()
            + p[0] * basis.get_tangent_u() +
            + p[1] * basis.get_tangent_v();

        // Compute the initial particle weight.
        Spectrum initial_flux = env_edf_value;
        initial_flux /= static_cast<float>(m_disk_point_prob * env_edf_prob);

        // Build the light ray.
        sampling_context.split_in_place(1, 1);
        const ShadingRay::Time time =
            ShadingRay::Time::create_with_normalized_time(
                sampling_context.next_double2(),
                m_shutter_open_time,
                m_shutter_close_time);
        const ShadingRay light_ray(
            ray_origin,
            -outgoing,
            time,
            VisibilityFlags::LightRay,
            0);

        // Build the path tracer.
        PathVisitor path_visitor(
            m_params,
            m_scene,
            m_frame,
            m_shading_context,
            sampling_context,
            samples,
            initial_flux);
        PathTracerType path_tracer(
            path_visitor,
            m_params.m_rr_min_path_length,
            m_params.m_max_path_length,
            m_params.m_max_iterations);

        // Trace the light path.
        const size_t path_length =
            path_tracer.trace(
                sampling_context,
                m_shading_context,
                light_ray);

        // Update path statistics.
        ++m_path_count;
        m_path_length.insert(path_length);

        // Return the number of samples generated when tracing this light path.
        return path_visitor.get_sample_count();
    }