Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[✨ feature] Scene: allow bypassing ray intersection acceleration data structures #297

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions include/mitsuba/render/interaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,20 @@ MI_DECLARE_ENUM_OPERATORS(RayFlags)

// -----------------------------------------------------------------------------

// TODO: move to DrJit if this is actually needed
namespace detail {
template <typename T, typename = int>
struct unmasked {
using type = T;
};
template <typename T>
struct unmasked<T, dr::enable_if_masked_array_t<T>> {
using type = typename T::Unmasked;
};
template <typename T>
using unmasked_t = typename unmasked<T>::type;
}

/**
* \brief Stores preliminary information related to a ray intersection
*
Expand All @@ -552,13 +566,16 @@ struct PreliminaryIntersection {
// =============================================================

using Float = Float_;
using ShapePtr = dr::replace_scalar_t<Float, const Shape_ *>;

MI_IMPORT_CORE_TYPES()

using Index = typename CoreAliases::UInt32;
using Ray3f = typename Shape_::Ray3f;
using Spectrum = typename Ray3f::Spectrum;

using UnmaskedShape = detail::unmasked_t<Shape_>;
using Ray3f = typename UnmaskedShape::Ray3f;
using Spectrum_ = typename Ray3f::Spectrum;
using Spectrum = std::conditional_t<dr::is_masked_array_v<Shape_>,
dr::masked_t<Spectrum_>, Spectrum_>;
using ShapePtr = dr::replace_scalar_t<Float, const UnmaskedShape *>;

//! @}
// =============================================================
Expand Down
12 changes: 12 additions & 0 deletions include/mitsuba/render/mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,18 @@ class MI_EXPORT_LIB Mesh : public Shape<Float, Spectrum> {
MI_DECLARE_RAY_INTERSECT_TRI_PACKET(8)
MI_DECLARE_RAY_INTERSECT_TRI_PACKET(16)

template <typename FloatP, typename Ray3fP>
std::tuple<FloatP, Point<FloatP, 2>, dr::uint32_array_t<FloatP>,
dr::uint32_array_t<FloatP>>
ray_intersect_preliminary_impl(const Ray3fP &ray,
dr::mask_t<FloatP> active) const;

template <typename FloatP, typename Ray3fP>
dr::mask_t<FloatP> ray_test_impl(const Ray3fP &ray,
dr::mask_t<FloatP> active) const;
MI_SHAPE_DEFINE_RAY_INTERSECT_METHODS()


#if defined(MI_ENABLE_EMBREE)
/// Return the Embree version of this shape
virtual RTCGeometry embree_geometry(RTCDevice device) override;
Expand Down
1 change: 1 addition & 0 deletions include/mitsuba/render/scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ class MI_EXPORT_LIB Scene : public Object {
ScalarFloat m_emitter_pmf;

bool m_shapes_grad_enabled;
bool m_use_naive_intersection;
};

/// Dummy function which can be called to ensure that the librender shared library is loaded
Expand Down
53 changes: 27 additions & 26 deletions include/mitsuba/render/shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -581,56 +581,57 @@ class MI_EXPORT_LIB Shape : public Object {
MI_EXTERN_CLASS(Shape)
NAMESPACE_END(mitsuba)

#define MI_IMPLEMENT_RAY_INTERSECT_PACKET(N) \
using typename Base::FloatP##N; \
using typename Base::UInt32P##N; \
using typename Base::MaskP##N; \
using typename Base::Point2fP##N; \
using typename Base::Point3fP##N; \
using typename Base::Ray3fP##N; \
std::tuple<FloatP##N, Point2fP##N, UInt32P##N, UInt32P##N> \
ray_intersect_preliminary_packet( \
const Ray3fP##N &ray, MaskP##N active) const override { \
(void) ray; (void) active; \
if constexpr (!dr::is_cuda_v<Float>) \
return ray_intersect_preliminary_impl<FloatP##N>(ray, active); \
#define MI_IMPLEMENT_RAY_INTERSECT_PACKET(N) \
std::tuple<typename Base::FloatP##N, typename Base::Point2fP##N, \
typename Base::UInt32P##N, typename Base::UInt32P##N> \
ray_intersect_preliminary_packet(const typename Base::Ray3fP##N &ray, \
typename Base::MaskP##N active) \
const override { \
(void) ray; \
(void) active; \
if constexpr (!dr::is_cuda_v<Float>) \
return ray_intersect_preliminary_impl<typename Base::FloatP##N>( \
ray, active); \
else \
Throw("ray_intersect_preliminary_packet() CUDA not supported"); \
} \
MaskP##N ray_test_packet(const Ray3fP##N &ray, MaskP##N active) \
typename Base::MaskP##N ray_test_packet( \
const typename Base::Ray3fP##N &ray, typename Base::MaskP##N active) \
const override { \
(void) ray; (void) active; \
if constexpr (!dr::is_cuda_v<Float>) \
return ray_test_impl<FloatP##N>(ray, active); \
(void) ray; \
(void) active; \
if constexpr (!dr::is_cuda_v<Float>) \
return ray_test_impl<typename Base::FloatP##N>(ray, active); \
else \
Throw("ray_intersect_preliminary_packet() CUDA not supported"); \
}

// Macro to define ray intersection methods given an *_impl() templated implementation
#define MI_SHAPE_DEFINE_RAY_INTERSECT_METHODS() \
#define MI_SHAPE_DEFINE_RAY_INTERSECT_METHODS() \
PreliminaryIntersection3f ray_intersect_preliminary( \
const Ray3f &ray, Mask active) const override { \
MI_MASK_ARGUMENT(active); \
PreliminaryIntersection3f pi = dr::zeros<PreliminaryIntersection3f>(); \
MI_MASK_ARGUMENT(active); \
PreliminaryIntersection3f pi = dr::zeros<PreliminaryIntersection3f>(); \
std::tie(pi.t, pi.prim_uv, pi.shape_index, pi.prim_index) = \
ray_intersect_preliminary_impl<Float>(ray, active); \
pi.shape = this; \
return pi; \
} \
Mask ray_test(const Ray3f &ray, Mask active) const override { \
MI_MASK_ARGUMENT(active); \
MI_MASK_ARGUMENT(active); \
return ray_test_impl<Float>(ray, active); \
} \
using typename Base::ScalarRay3f; \
std::tuple<ScalarFloat, ScalarPoint2f, ScalarUInt32, ScalarUInt32> \
ray_intersect_preliminary_scalar(const ScalarRay3f &ray) const override { \
ray_intersect_preliminary_scalar(const typename Base::ScalarRay3f &ray) \
const override { \
return ray_intersect_preliminary_impl<ScalarFloat>(ray, true); \
} \
ScalarMask ray_test_scalar(const ScalarRay3f &ray) const override { \
ScalarMask ray_test_scalar(const typename Base::ScalarRay3f &ray) \
const override { \
return ray_test_impl<ScalarFloat>(ray, true); \
} \
MI_IMPLEMENT_RAY_INTERSECT_PACKET(4) \
MI_IMPLEMENT_RAY_INTERSECT_PACKET(8) \
MI_IMPLEMENT_RAY_INTERSECT_PACKET(4) \
MI_IMPLEMENT_RAY_INTERSECT_PACKET(8) \
MI_IMPLEMENT_RAY_INTERSECT_PACKET(16)

// -----------------------------------------------------------------------
Expand Down
39 changes: 39 additions & 0 deletions src/render/mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,45 @@ Mesh<Float, Spectrum>::barycentric_coordinates(const SurfaceInteraction3f &si,
return {w, u, v};
}

MI_VARIANT
template <typename FloatP, typename Ray3fP>
std::tuple<FloatP, Point<FloatP, 2>, dr::uint32_array_t<FloatP>,
dr::uint32_array_t<FloatP>>
Mesh<Float, Spectrum>::ray_intersect_preliminary_impl(
const Ray3fP &ray, dr::mask_t<FloatP> active) const {
MI_MASK_ARGUMENT(active);

FloatP t = dr::Infinity<FloatP>;
Point<FloatP, 2> uv = dr::NaN<FloatP>;
dr::uint32_array_t<FloatP> prim_index = (uint32_t) -1;
for (size_t index = 0; index < m_face_count; ++index) {
auto [prim_t, prim_uv] = ray_intersect_triangle_impl<FloatP>(index, ray, active);
dr::mask_t<FloatP> valid = dr::isfinite(prim_t) && (prim_t < t);
dr::masked(t, valid) = prim_t;
dr::masked(uv, valid) = prim_uv;
dr::masked(prim_index, valid) = index;
}

// Cannot determine the shape index, will be up to the caller.
uint32_t shape_index = (uint32_t) -1;
return { t, uv, shape_index, prim_index };
}

MI_VARIANT
template <typename FloatP, typename Ray3fP>
dr::mask_t<FloatP>
Mesh<Float, Spectrum>::ray_test_impl(const Ray3fP &ray,
dr::mask_t<FloatP> active) const {
MI_MASK_ARGUMENT(active);

dr::mask_t<FloatP> hit = false;
for (size_t index = 0; index < m_face_count; ++index) {
FloatP prim_t = ray_intersect_triangle_impl<FloatP>(index, ray, active).first;
hit |= dr::neq(prim_t, dr::Infinity<FloatP>);
}

return hit;
}

MI_VARIANT typename Mesh<Float, Spectrum>::SurfaceInteraction3f
Mesh<Float, Spectrum>::compute_surface_interaction(const Ray3f &ray,
Expand Down
90 changes: 71 additions & 19 deletions src/render/scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,19 @@ MI_VARIANT Scene<Float, Spectrum>::Scene(const Properties &props) {
for (Sensor *sensor: m_sensors)
sensor->set_scene(this);

if constexpr (dr::is_cuda_v<Float>)
accel_init_gpu(props);
else
accel_init_cpu(props);
// Decide whether to use acceleration data structures for ray intersections
m_use_naive_intersection = props.get<bool>("use_naive_intersection", false);
if (m_use_naive_intersection)
Log(Info, "The scene will not use acceleration data structures "
"for ray intersections.");

// Build acceleration data structures if needed
if (!m_use_naive_intersection) {
if constexpr (dr::is_cuda_v<Float>)
accel_init_gpu(props);
else
accel_init_cpu(props);
}

if (!m_emitters.empty()) {
// Inform environment emitters etc. about the scene bounds
Expand All @@ -84,10 +93,12 @@ MI_VARIANT Scene<Float, Spectrum>::Scene(const Properties &props) {
}

MI_VARIANT Scene<Float, Spectrum>::~Scene() {
if constexpr (dr::is_cuda_v<Float>)
accel_release_gpu();
else
accel_release_cpu();
if (!m_use_naive_intersection) {
if constexpr (dr::is_cuda_v<Float>)
accel_release_gpu();
else
accel_release_cpu();
}

// Trigger deallocation of all instances
m_emitters.clear();
Expand All @@ -111,6 +122,21 @@ Scene<Float, Spectrum>::ray_intersect(const Ray3f &ray, uint32_t ray_flags, Mask
MI_MASKED_FUNCTION(ProfilerPhase::RayIntersect, active);
DRJIT_MARK_USED(coherent);

if (m_use_naive_intersection) {
// Naive intersection mode: bypass all acceleration data structures,
// test for intersections explicitly against each shape.
PreliminaryIntersection3f pi = dr::zeros<PreliminaryIntersection3f>();

for (size_t shape_index = 0; shape_index < m_shapes.size(); ++shape_index) {
PreliminaryIntersection3f prim_pi =
m_shapes[shape_index]->ray_intersect_preliminary(ray, active);
Mask valid = prim_pi.is_valid() && prim_pi.t < pi.t;
dr::masked(pi, valid) = prim_pi;
}
return pi.compute_surface_interaction(ray, ray_flags,
active && pi.is_valid());
}

if constexpr (dr::is_cuda_v<Float>)
return ray_intersect_gpu(ray, ray_flags, active);
else
Expand All @@ -120,6 +146,22 @@ Scene<Float, Spectrum>::ray_intersect(const Ray3f &ray, uint32_t ray_flags, Mask
MI_VARIANT typename Scene<Float, Spectrum>::PreliminaryIntersection3f
Scene<Float, Spectrum>::ray_intersect_preliminary(const Ray3f &ray, Mask coherent, Mask active) const {
DRJIT_MARK_USED(coherent);

if (m_use_naive_intersection) {
// Naive intersection mode: bypass all acceleration data structures,
// test for intersections explicitly against each shape.
PreliminaryIntersection3f pi = dr::zeros<PreliminaryIntersection3f>();

for (size_t shape_index = 0; shape_index < m_shapes.size(); ++shape_index) {
PreliminaryIntersection3f prim_pi =
m_shapes[shape_index]->ray_intersect_preliminary(ray, active);
Mask valid = prim_pi.is_valid() && (prim_pi.t < pi.t) && (prim_pi.t > 0.f);
dr::masked(pi, valid) = prim_pi;
}

return pi;
}

if constexpr (dr::is_cuda_v<Float>)
return ray_intersect_preliminary_gpu(ray, active);
else
Expand All @@ -131,6 +173,14 @@ Scene<Float, Spectrum>::ray_test(const Ray3f &ray, Mask coherent, Mask active) c
MI_MASKED_FUNCTION(ProfilerPhase::RayTest, active);
DRJIT_MARK_USED(coherent);

if (m_use_naive_intersection) {
Mask hit = false;
for (const auto &shape : m_shapes) {
hit |= shape->ray_test(ray, active);
}
return hit;
}

if constexpr (dr::is_cuda_v<Float>)
return ray_test_gpu(ray, active);
else
Expand Down Expand Up @@ -299,18 +349,20 @@ MI_VARIANT void Scene<Float, Spectrum>::parameters_changed(const std::vector<std
if (m_environment)
m_environment->set_scene(this); // TODO use parameters_changed({"scene"})

bool accel_is_dirty = false;
for (auto &s : m_shapes) {
accel_is_dirty = s->dirty();
if (accel_is_dirty)
break;
}
if (!m_use_naive_intersection) {
bool accel_is_dirty = false;
for (auto &s : m_shapes) {
accel_is_dirty = s->dirty();
if (accel_is_dirty)
break;
}

if (accel_is_dirty) {
if constexpr (dr::is_cuda_v<Float>)
accel_parameters_changed_gpu();
else
accel_parameters_changed_cpu();
if (accel_is_dirty) {
if constexpr (dr::is_cuda_v<Float>)
accel_parameters_changed_gpu();
else
accel_parameters_changed_cpu();
}
}

// Check whether any shape parameters have gradient tracking enabled
Expand Down
Loading