cocos-engine-external/sources/enoki/array_call.h

292 lines
13 KiB
C++

/*
enoki/array_call.h -- Enoki arrays of pointers, support for
array (virtual) method calls
Copyright (c) 2019 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
*/
#pragma once
#include <enoki/array_generic.h>
NAMESPACE_BEGIN(enoki)
template <typename Class, typename Storage> struct call_support {
call_support(const Storage &) { }
};
template <typename Value_, size_t Size_, bool IsMask_, typename Derived_>
struct StaticArrayImpl<Value_, Size_, IsMask_, Derived_,
enable_if_t<detail::array_config<Value_, Size_>::use_pointer_impl>>
: StaticArrayImpl<uintptr_t, Size_, IsMask_, Derived_> {
using UnderlyingType = std::uintptr_t;
using Base = StaticArrayImpl<UnderlyingType, Size_, IsMask_, Derived_>;
ENOKI_ARRAY_DEFAULTS(StaticArrayImpl)
using Base::derived;
using Value = std::conditional_t<IsMask_, typename Base::Value, Value_>;
using Scalar = std::conditional_t<IsMask_, typename Base::Scalar, Value_>;
StaticArrayImpl() = default;
StaticArrayImpl(Value value) : Base(UnderlyingType(value)) { }
StaticArrayImpl(std::nullptr_t) : Base(UnderlyingType(0)) { }
template <typename T, enable_if_t<!std::is_pointer_v<T>> = 0>
StaticArrayImpl(const T &b) : Base(b) { }
template <typename T>
StaticArrayImpl(const T &b, detail::reinterpret_flag)
: Base(b, detail::reinterpret_flag()) { }
template <typename T1, typename T2, typename T = StaticArrayImpl, enable_if_t<
array_depth_v<T1> == array_depth_v<T> && array_size_v<T1> == Base::Size1 &&
array_depth_v<T2> == array_depth_v<T> && array_size_v<T2> == Base::Size2 &&
Base::Size2 != 0> = 0>
StaticArrayImpl(const T1 &a1, const T2 &a2)
: Base(a1, a2) { }
ENOKI_INLINE decltype(auto) coeff(size_t i) const {
using Coeff = decltype(Base::coeff(i));
if constexpr (std::is_same_v<Coeff, const typename Base::Value &>)
return (const Value &) Base::coeff(i);
else
return Base::coeff(i);
}
ENOKI_INLINE decltype(auto) coeff(size_t i) {
using Coeff = decltype(Base::coeff(i));
if constexpr (std::is_same_v<Coeff, typename Base::Value &>)
return (Value &) Base::coeff(i);
else
return Base::coeff(i);
}
template <typename T, typename Mask>
ENOKI_INLINE size_t compress_(T *&ptr, const Mask &mask) const {
return Base::compress_((UnderlyingType *&) ptr, mask);
}
auto operator->() const {
using BaseType = std::decay_t<std::remove_pointer_t<scalar_t<Derived_>>>;
return call_support<BaseType, Derived_>(derived());
}
template <typename T> Derived_& operator=(T&& t) {
ENOKI_MARK_USED(t);
if constexpr (std::is_same_v<T, std::nullptr_t>)
return (Derived_ &) Base::operator=(UnderlyingType(0));
else if constexpr (std::is_convertible_v<T, Value>)
return (Derived_ &) Base::operator=(UnderlyingType(t));
else
return (Derived_ &) Base::operator=(std::forward<T>(t));
}
};
NAMESPACE_BEGIN(detail)
template <typename, template <typename...> typename T, typename... Args>
struct is_callable : std::false_type {};
template <template <typename...> typename T, typename... Args>
struct is_callable<std::void_t<T<Args...>>, T, Args...> : std::true_type { };
template <template <typename...> typename T, typename... Args>
constexpr bool is_callable_v = is_callable<void, T, Args...>::value;
template <typename Guide, typename Result, typename = int> struct vectorize_result {
using type = Result;
};
template <typename Guide, typename Result> struct vectorize_result<Guide, Result, enable_if_t<is_scalar_v<Result>>> {
using type = replace_scalar_t<array_t<Guide>, Result, false>;
};
template <typename T, typename Perm>
decltype(auto) gather_helper(T&& v, const Perm &perm) {
ENOKI_MARK_USED(perm);
using DT = std::decay_t<T>;
if constexpr (!is_cuda_array_v<DT> && !std::is_class_v<DT>)
return v;
else
return gather<std::decay_t<DT>, 0, true, true>(v, perm);
}
template <typename Storage_> struct call_support_base {
using Storage = Storage_;
using InstancePtr = value_t<Storage_>;
using Mask = mask_t<Storage_>;
call_support_base(const Storage &self) : self(self) { }
const Storage &self;
template <typename Func, typename InputMask,
typename Tuple, size_t ... Indices>
ENOKI_INLINE auto dispatch(Func func, InputMask mask_, Tuple tuple,
std::index_sequence<Indices...>) const {
Mask mask = Mask(mask_) & neq(self, nullptr);
using FuncResult = decltype(func(
std::declval<InstancePtr>(),
mask,
std::get<Indices>(tuple)...
));
if constexpr (!std::is_void_v<FuncResult>) {
using Result = typename vectorize_result<Mask, FuncResult>::type;
Result result = zero<Result>(self.size());
if constexpr (!is_cuda_array_v<Storage>) {
while (any(mask)) {
InstancePtr value = extract(self, mask);
Mask active = mask & eq(self, value);
mask = andnot(mask, active);
masked(result, active) = func(value, active, std::get<Indices>(tuple)...);
}
} else {
auto partitioned = partition(self);
if (partitioned.size() == 1 && partitioned[0].first != nullptr) {
result = func(partitioned[0].first, true,
std::get<Indices>(tuple)...);
} else {
for (auto [value, permutation] : partitioned) {
if (value == nullptr)
continue;
Result temp = func(value, gather_helper(mask, permutation),
gather_helper(std::get<Indices>(tuple),
permutation)...);
scatter<0, true, true>(result, temp, permutation);
}
}
}
return result;
} else {
if constexpr (!is_cuda_array_v<Storage>) {
while (any(mask)) {
InstancePtr value = extract(self, mask);
Mask active = mask & eq(self, value);
mask = andnot(mask, active);
func(value, active, std::get<Indices>(tuple)...);
}
} else {
auto partitioned = partition(self);
if (partitioned.size() == 1 && partitioned[0].first != nullptr) {
func(partitioned[0].first, true, std::get<Indices>(tuple)...);
} else {
for (auto [value, permutation] : partitioned) {
if (value == nullptr)
continue;
func(value, gather_helper(mask, permutation),
gather_helper(std::get<Indices>(tuple),
permutation)...);
}
}
}
}
}
};
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-value"
#endif
template <typename... Ts>
inline constexpr bool last_of(Ts... values) { return (false, ..., values); }
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
NAMESPACE_END(detail)
#define ENOKI_CALL_SUPPORT_FRIEND() \
template <typename, typename> friend struct enoki::call_support;
#define ENOKI_CALL_SUPPORT_BEGIN(Class_) \
namespace enoki { \
template <typename Storage> \
struct call_support<Class_, Storage> : detail::call_support_base<Storage> {\
using Base = detail::call_support_base<Storage>; \
using Base::Base; \
using typename Base::Mask; \
using Class = Class_; \
using typename Base::InstancePtr; \
using Base::self; \
auto operator-> () { return this; }
#define ENOKI_CALL_SUPPORT_TEMPLATE_BEGIN(Class_) \
namespace enoki { \
template <typename Storage, typename... Ts> \
struct call_support<Class_<Ts...>, Storage> \
: detail::call_support_base<Storage> { \
using Base = detail::call_support_base<Storage>; \
using Base::Base; \
using typename Base::Mask; \
using Class = Class_<Ts...>; \
using typename Base::InstancePtr; \
using Base::self; \
auto operator-> () { return this; }
#define ENOKI_CALL_SUPPORT_METHOD(func) \
private: \
template <typename... Args> \
using __##func##_t = \
decltype(std::declval<InstancePtr>()->func(std::declval<Args>()...)); \
\
public: \
template <typename... Args> auto func(Args&&... args) const { \
auto lambda = [](InstancePtr instance, const Mask &mask, \
auto &&... a) ENOKI_INLINE_LAMBDA { \
ENOKI_MARK_USED(mask); \
/* Does the method accept a mask argument? If so, provide. */ \
if constexpr (detail::is_callable_v<__##func##_t, decltype(a)..., \
Mask>) \
return instance->func(a..., mask); \
else \
return instance->func(a...); \
}; \
/* Was a mask provided to this function? If not, set to all ones. */ \
auto args_tuple = std::tie(args...); \
if constexpr (detail::last_of(is_mask_v<Args>...)) { \
return Base::dispatch( \
lambda, std::get<sizeof...(Args) - 1>(args_tuple), args_tuple, \
std::make_index_sequence<sizeof...(Args) - 1>()); \
} else { \
return Base::dispatch( \
lambda, true, args_tuple, \
std::make_index_sequence<sizeof...(Args)>()); \
} \
}
#define ENOKI_CALL_SUPPORT_GETTER_TYPE(name, field, type) \
template < \
typename Field = decltype(Class::field), \
typename Return = replace_scalar_t<Storage, type, false>> \
Return name(Mask mask = true) const { \
using IntType = replace_scalar_t<Storage, std::uintptr_t, false>; \
auto offset = \
IntType(self) + (std::uintptr_t) &(((Class *) nullptr)->field); \
mask &= neq(self, nullptr); \
return gather<Return, 1>(nullptr, offset, mask); \
}
#define ENOKI_CALL_SUPPORT_GETTER(name, field) \
ENOKI_CALL_SUPPORT_GETTER_TYPE(name, field, Field)
#define ENOKI_CALL_SUPPORT_END(Name) \
}; \
}
#define ENOKI_CALL_SUPPORT_TEMPLATE_END(Name) \
ENOKI_CALL_SUPPORT_END(Name)
NAMESPACE_END(enoki)