6#ifndef HALIDE_RUNTIME_BUFFER_H
7#define HALIDE_RUNTIME_BUFFER_H
20#include <AvailabilityVersions.h>
21#include <TargetConditionals.h>
24#if defined(__has_feature)
25#if __has_feature(memory_sanitizer)
26#include <sanitizer/msan_interface.h>
34#define HALIDE_ALLOCA _alloca
36#define HALIDE_ALLOCA __builtin_alloca
40#if __GNUC__ == 5 && __GNUC_MINOR__ == 1
41#pragma GCC diagnostic ignored "-Warray-bounds"
44#ifndef HALIDE_RUNTIME_BUFFER_CHECK_INDICES
45#define HALIDE_RUNTIME_BUFFER_CHECK_INDICES 0
48#ifndef HALIDE_RUNTIME_BUFFER_ALLOCATION_ALIGNMENT
52#define HALIDE_RUNTIME_BUFFER_ALLOCATION_ALIGNMENT 128
56 "HALIDE_RUNTIME_BUFFER_ALLOCATION_ALIGNMENT must be a power of 2.");
64#ifndef HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC
71 #define HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC 0
73#elif defined(__ANDROID_API__) && __ANDROID_API__ < 28
76 #define HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC 0
78#elif defined(__APPLE__)
80 #if TARGET_OS_OSX && (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_15)
83 #define HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC 0
85 #elif TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_14_0)
88 #define HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC 0
93 #define HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC 1
99 #if defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC)
102 #define HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC 0
107 #define HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC 1
120template<
typename T,
int Dims,
int InClassDimStorage>
125template<
typename...
Args>
131template<
typename T,
typename...
Args>
133 static const bool value = std::is_convertible<T, int>::value &&
AllInts<
Args...>::value;
139template<
typename...
Args>
142template<
typename...
Args>
147template<
typename Container>
158 static inline void *(*default_allocate_fn)(
size_t) =
nullptr;
218template<
typename T = void,
237 static const bool T_is_void = std::is_same<typename std::remove_const<T>::type,
void>::value;
240 template<
typename T2>
241 using add_const_if_T_is_const =
typename std::conditional<std::is_const<T>::value,
const T2,
T2>::type;
245 using not_void_T =
typename std::conditional<T_is_void,
246 add_const_if_T_is_const<uint8_t>,
250 using not_const_T =
typename std::remove_const<T>::type;
256 using storage_T =
typename std::conditional<std::is_pointer<T>::value,
uint64_t, not_void_T>::type;
260 static constexpr bool has_static_halide_type = !T_is_void;
270 return alloc !=
nullptr;
281 static_assert(!has_static_dimensions || static_dimensions() >= 0);
285 void incref()
const {
286 if (owns_host_memory()) {
290 if (!dev_ref_count) {
296 dev_ref_count =
new DeviceRefCount;
298 dev_ref_count->
count++;
304 struct DevRefCountCropped : DeviceRefCount {
311 Buffer<T, AnyDims> cropped_from;
312 explicit DevRefCountCropped(
const Buffer<T, AnyDims> &cropped_from)
313 : cropped_from(cropped_from) {
314 ownership = BufferDeviceOwnership::Cropped;
319 void crop_from(
const Buffer<T, AnyDims> &cropped_from) {
320 assert(dev_ref_count ==
nullptr);
321 dev_ref_count =
new DevRefCountCropped(cropped_from);
331 alloc->~AllocationHeader();
336 set_host_dirty(
false);
344 assert(!(alloc && device_dirty()) &&
345 "Implicitly freeing a dirty device allocation while a host allocation still lives. "
346 "Call device_free explicitly if you want to drop dirty device-side data. "
347 "Call copy_to_host explicitly if you want the data copied to the host allocation "
348 "before the device allocation is freed.");
365 delete (DevRefCountCropped *)dev_ref_count;
367 delete dev_ref_count;
371 dev_ref_count =
nullptr;
376 void free_shape_storage() {
377 if (buf.
dim != shape) {
383 template<
int DimsSpecified>
384 void make_static_shape_storage() {
386 "Number of arguments to Buffer() does not match static dimensionality");
401 void make_shape_storage(
const int dimensions) {
403 assert(
false &&
"Number of arguments to Buffer() does not match static dimensionality");
413 make_shape_storage(
other.dimensions);
417 template<
typename T2,
int D2,
int S2>
420 copy_shape_from(
other.buf);
423 other.buf.dim =
nullptr;
434 dev_ref_count =
new DeviceRefCount;
440 void initialize_shape(
const int *
sizes) {
453 void initialize_shape(
const std::vector<int> &
sizes) {
455 initialize_shape(
sizes.data());
459 template<
typename Array,
size_t N>
460 void initialize_shape_from_array_shape(
int next,
Array (&
vals)[
N]) {
466 initialize_shape_from_array_shape(next - 1,
vals[0]);
472 template<
typename T2>
473 void initialize_shape_from_array_shape(
int,
const T2 &) {
477 template<
typename Array,
size_t N>
478 static int dimensionality_of_array(
Array (&
vals)[
N]) {
479 return dimensionality_of_array(
vals[0]) + 1;
482 template<
typename T2>
483 static int dimensionality_of_array(
const T2 &) {
488 template<
typename Array,
size_t N>
490 return scalar_type_of_array(
vals[0]);
493 template<
typename T2>
499 void crop_host(
int d,
int min,
int extent) {
501 assert(dim(d).
max() >= min + extent - 1);
503 if (buf.
host !=
nullptr) {
504 buf.
host += (
shift * dim(d).stride()) * type().bytes();
511 void crop_host(
const std::vector<std::pair<int, int>> &rect) {
512 assert(rect.size() <=
static_cast<decltype(rect.size())
>(std::numeric_limits<int>::max()));
516 crop_host(
i, rect[
i].first, rect[
i].
second);
536 void slice_host(
int d,
int pos) {
539 assert(d >= 0 && d < dimensions());
543 if (buf.
host !=
nullptr) {
595 return min() + extent() - 1;
606 return val != other.
val;
621 return {min() + extent()};
641 return dim(
i).extent();
644 return dim(
i).stride();
651 return buf.number_of_elements();
656 if constexpr (has_static_dimensions) {
672 return (T *)buf.begin();
678 return (T *)buf.end();
683 return buf.size_in_bytes();
695 buf.
type = static_halide_type();
705 assert(T_is_void || buf.
type == static_halide_type());
706 initialize_from_buffer(buf, ownership);
710 template<
typename T2,
int D2,
int S2>
714 template<
typename T2,
int D2,
int S2>
715 static void static_assert_can_convert_from() {
716 static_assert((!std::is_const<T2>::value || std::is_const<T>::value),
717 "Can't convert from a Buffer<const T> to a Buffer<T>");
718 static_assert(std::is_same<typename std::remove_const<T>::type,
719 typename std::remove_const<T2>::type>::value ||
721 "type mismatch constructing Buffer");
723 "Can't convert from a Buffer with static dimensionality to a Buffer with different static dimensionality");
737 template<
typename T2,
int D2,
int S2>
741 if (
other.type() != static_halide_type()) {
755 template<
typename T2,
int D2,
int S2>
769 dev_ref_count =
other.dev_ref_count;
770 copy_shape_from(
other.buf);
779 template<
typename T2,
int D2,
int S2>
783 assert_can_convert_from(
other);
785 dev_ref_count =
other.dev_ref_count;
786 copy_shape_from(
other.buf);
793 dev_ref_count(
other.dev_ref_count) {
794 other.dev_ref_count =
nullptr;
795 other.alloc =
nullptr;
802 template<
typename T2,
int D2,
int S2>
806 dev_ref_count(
other.dev_ref_count) {
807 assert_can_convert_from(
other);
808 other.dev_ref_count =
nullptr;
809 other.alloc =
nullptr;
816 template<
typename T2,
int D2,
int S2>
818 if ((
const void *)
this == (
const void *)&
other) {
821 assert_can_convert_from(
other);
824 dev_ref_count =
other.dev_ref_count;
826 free_shape_storage();
828 copy_shape_from(
other.buf);
835 if ((
const void *)
this == (
const void *)&
other) {
840 dev_ref_count =
other.dev_ref_count;
842 free_shape_storage();
844 copy_shape_from(
other.buf);
851 template<
typename T2,
int D2,
int S2>
853 assert_can_convert_from(
other);
856 other.alloc =
nullptr;
857 dev_ref_count =
other.dev_ref_count;
858 other.dev_ref_count =
nullptr;
859 free_shape_storage();
869 other.alloc =
nullptr;
870 dev_ref_count =
other.dev_ref_count;
871 other.dev_ref_count =
nullptr;
872 free_shape_storage();
880 size_t size = type().bytes();
881 for (
int i = 0;
i < dimensions();
i++) {
882 size *= dim(
i).extent();
886 for (
int i = 0;
i < dimensions();
i++) {
887 size /= dim(
i).extent();
889 assert(size == (
size_t)type().bytes() &&
"Error: Overflow computing total size of buffer.");
895 void (*deallocate_fn)(
void *) =
nullptr) {
904 const auto align_up = [=](
size_t value) ->
size_t {
905 return (value + alignment - 1) & ~(alignment - 1);
908 size_t size = size_in_bytes();
910#if HALIDE_RUNTIME_BUFFER_USE_ALIGNED_ALLOC
932 if (!deallocate_fn) {
934 if (!deallocate_fn) {
935 deallocate_fn =
free;
947 (
int)
sizeof(std::max_align_t)));
973 template<
typename...
Args,
974 typename =
typename std::enable_if<
AllInts<
Args...>::value>::type>
977 assert(static_halide_type() == t);
979 int extents[] = {first, (
int)rest...};
983 initialize_shape(extents);
997 static_assert(!T_is_void,
998 "To construct an Buffer<void>, pass a halide_type_t as the first argument to the constructor");
999 int extents[] = {first};
1000 buf.
type = static_halide_type();
1003 initialize_shape(extents);
1010 template<
typename...
Args,
1011 typename =
typename std::enable_if<
AllInts<
Args...>::value>::type>
1013 static_assert(!T_is_void,
1014 "To construct an Buffer<void>, pass a halide_type_t as the first argument to the constructor");
1015 int extents[] = {first,
second, (
int)rest...};
1016 buf.
type = static_halide_type();
1019 initialize_shape(extents);
1030 assert(static_halide_type() == t);
1034 make_shape_storage((
int)
sizes.size());
1035 initialize_shape(
sizes);
1049 static std::vector<int> make_ordered_sizes(
const std::vector<int> &
sizes,
const std::vector<int> &order) {
1052 for (
size_t i = 0;
i <
sizes.size(); ++
i) {
1074 template<
typename Array,
size_t N>
1077 buf.
type = scalar_type_of_array(
vals);
1087 template<
typename...
Args,
1088 typename =
typename std::enable_if<
AllInts<
Args...>::value>::type>
1091 assert(static_halide_type() == t);
1093 int extents[] = {first, (
int)rest...};
1098 initialize_shape(extents);
1104 template<
typename...
Args,
1105 typename =
typename std::enable_if<
AllInts<
Args...>::value>::type>
1107 int extents[] = {first, (
int)rest...};
1108 buf.
type = static_halide_type();
1112 initialize_shape(extents);
1120 buf.
type = static_halide_type();
1122 make_shape_storage((
int)
sizes.size());
1123 initialize_shape(
sizes);
1132 assert(static_halide_type() == t);
1136 make_shape_storage((
int)
sizes.size());
1137 initialize_shape(
sizes);
1145 assert(static_halide_type() == t);
1149 make_shape_storage(d);
1150 for (
int i = 0;
i < d;
i++) {
1151 buf.
dim[
i] = shape[
i];
1159 const std::vector<halide_dimension_t> &shape)
1160 :
Buffer(t, data, (
int)shape.size(), shape.data()) {
1167 buf.
type = static_halide_type();
1169 make_shape_storage(d);
1170 for (
int i = 0;
i < d;
i++) {
1171 buf.
dim[
i] = shape[
i];
1178 explicit Buffer(T *data,
const std::vector<halide_dimension_t> &shape)
1179 :
Buffer(data, (
int)shape.size(), shape.data()) {
1187 free_shape_storage();
1214 template<
typename T2,
int D2 = Dims>
1227 template<
typename T2,
int D2 = Dims>
1240 template<
typename T2,
int D2 = Dims>
1276 template<
typename TVoid,
1278 typename =
typename std::enable_if<std::is_same<TVoid, void>::value &&
1279 !std::is_void<T2>::value &&
1280 !std::is_const<T2>::value>::type>
1287 template<
typename TVoid,
1289 typename =
typename std::enable_if<std::is_same<TVoid, void>::value &&
1290 !std::is_void<T2>::value &&
1291 std::is_const<T2>::value>::type>
1299 return (dimensions() > 0) ? dim(0).extent() : 1;
1302 return (dimensions() > 1) ? dim(1).extent() : 1;
1305 return (dimensions() > 2) ? dim(2).extent() : 1;
1312 return dim(0).min();
1316 return dim(0).max();
1320 return dim(1).min();
1324 return dim(1).max();
1341 void (*deallocate_fn)(
void *) =
nullptr)
const {
1352 void (*deallocate_fn)(
void *) =
nullptr)
const {
1354 assert(dimensions() == 3);
1356 dst.
set_min(min(0), min(1), min(2));
1366 void (*deallocate_fn)(
void *) =
nullptr)
const {
1367 std::vector<int> mins, extents;
1368 const int dims = dimensions();
1370 extents.reserve(dims);
1371 for (
int d = 0; d < dims; ++d) {
1372 mins.push_back(dim(d).min());
1373 extents.push_back(dim(d).extent());
1404 template<
typename T2,
int D2,
int S2>
1406 static_assert(!std::is_const<T>::value,
"Cannot call copy_from() on a Buffer<const T>");
1407 assert(!device_dirty() &&
"Cannot call Halide::Runtime::Buffer::copy_from on a device dirty destination.");
1408 assert(!src.
device_dirty() &&
"Cannot call Halide::Runtime::Buffer::copy_from on a device dirty source.");
1416 const int d = dimensions();
1417 for (
int i = 0;
i < d;
i++) {
1432 if (T_is_void ? (type().bytes() == 1) : (
sizeof(not_void_T) == 1)) {
1437 }
else if (T_is_void ? (type().bytes() == 2) : (
sizeof(not_void_T) == 2)) {
1442 }
else if (T_is_void ? (type().bytes() == 4) : (
sizeof(not_void_T) == 4)) {
1447 }
else if (T_is_void ? (type().bytes() == 8) : (
sizeof(not_void_T) == 8)) {
1453 assert(
false &&
"type().bytes() must be 1, 2, 4, or 8");
1470 im.device_deallocate();
1472 im.crop_host(d, min, extent);
1474 complete_device_crop(
im);
1482 void crop(
int d,
int min,
int extent) {
1488 *
this = cropped(d, min, extent);
1490 crop_host(d, min, extent);
1506 im.device_deallocate();
1510 complete_device_crop(
im);
1519 void crop(
const std::vector<std::pair<int, int>> &rect) {
1525 *
this = cropped(rect);
1537 im.translate(d,
dx);
1545 device_deallocate();
1560 device_deallocate();
1572 assert(mins.size() <=
static_cast<decltype(mins.size())
>(dimensions()));
1573 device_deallocate();
1574 for (
size_t i = 0;
i < mins.size();
i++) {
1579 template<
typename...
Args>
1581 set_min(std::vector<int>{args...});
1589 for (
size_t i = 0;
i <
coords.size();
i++) {
1597 template<
typename...
Args>
1599 return contains(std::vector<int>{args...});
1631 assert((
int)order.size() == dimensions());
1632 if (dimensions() < 2) {
1641 transpose(
j,
j - 1);
1650 im.transpose(order);
1658 static_assert(
Dims ==
AnyDims ||
Dims > 0,
"Cannot slice a 0-dimensional buffer");
1659 assert(dimensions() > 0);
1666 im.device_deallocate();
1668 im.slice_host(d,
pos);
1670 complete_device_slice(
im, d,
pos);
1679 static_assert(
Dims ==
AnyDims ||
Dims > 0,
"Cannot slice a 0-dimensional buffer");
1680 assert(dimensions() > 0);
1682 return sliced(d, dim(d).min());
1691 static_assert(
Dims ==
AnyDims,
"Cannot call slice() on a Buffer with static dimensionality.");
1692 assert(dimensions() > 0);
1699 *
this = sliced(d,
pos);
1707 slice(d, dim(d).min());
1730 static_assert(
Dims ==
AnyDims,
"Cannot call embed() on a Buffer with static dimensionality.");
1731 assert(d >= 0 && d <= dimensions());
1733 translate(dimensions() - 1,
pos);
1734 for (
int i = dimensions() - 1;
i > d;
i--) {
1735 transpose(
i,
i - 1);
1744 static_assert(
Dims ==
AnyDims,
"Cannot call add_dimension() on a Buffer with static dimensionality.");
1747 if (buf.
dim != shape) {
1750 for (
int i = 0;
i < dims;
i++) {
1758 for (
int i = 0;
i < dims;
i++) {
1759 buf.
dim[
i] = shape[
i];
1764 buf.
dim[dims] = {0, 1, 0};
1786 assert((!v || !device_dirty()) &&
"Cannot set host dirty when device is already dirty. Call copy_to_host() before accessing the buffer from host.");
1787 buf.set_host_dirty(v);
1795 return buf.device_dirty();
1799 return buf.host_dirty();
1803 assert((!v || !host_dirty()) &&
"Cannot set device dirty when host is already dirty.");
1804 buf.set_device_dirty(v);
1808 if (device_dirty()) {
1826 if (dev_ref_count) {
1828 "Can't call device_free on an unmanaged or wrapped native device handle. "
1829 "Free the source allocation or call device_detach_native instead.");
1832 "Multiple Halide::Runtime::Buffer objects share this device "
1833 "allocation. Freeing it would create dangling references. "
1834 "Don't call device_free on Halide buffers that you have copied or "
1835 "passed by value.");
1841 if (dev_ref_count) {
1842 delete dev_ref_count;
1843 dev_ref_count =
nullptr;
1850 assert(device_interface);
1853 return device_interface->
wrap_native(
ctx, &buf, handle, device_interface);
1859 "Only call device_detach_native on buffers wrapping a native "
1860 "device handle via device_wrap_native. This buffer was allocated "
1861 "using device_malloc, or is unmanaged. "
1862 "Call device_free or free the original allocation instead.");
1865 "Multiple Halide::Runtime::Buffer objects share this device "
1866 "allocation. Freeing it could create dangling references. "
1867 "Don't call device_detach_native on Halide buffers that you "
1868 "have copied or passed by value.");
1873 delete dev_ref_count;
1874 dev_ref_count =
nullptr;
1883 if (dev_ref_count) {
1885 "Can't call device_and_host_free on a device handle not allocated with device_and_host_malloc. "
1886 "Free the source allocation or call device_detach_native instead.");
1889 "Multiple Halide::Runtime::Buffer objects share this device "
1890 "allocation. Freeing it would create dangling references. "
1891 "Don't call device_and_host_free on Halide buffers that you have copied or "
1892 "passed by value.");
1898 if (dev_ref_count) {
1899 delete dev_ref_count;
1900 dev_ref_count =
nullptr;
1906 return buf.device_sync(
ctx);
1915 if (dev_ref_count ==
nullptr) {
1929 static_assert(
Dims ==
AnyDims ||
Dims == 3,
"make_interleaved() must be called on a Buffer that can represent 3 dimensions.");
1945 return make_interleaved(static_halide_type(), width, height, channels);
1951 static_assert(
Dims ==
AnyDims ||
Dims == 3,
"make_interleaved() must be called on a Buffer that can represent 3 dimensions.");
1960 return make_interleaved(static_halide_type(), data, width, height, channels);
1965 static_assert(
Dims ==
AnyDims ||
Dims == 0,
"make_scalar() must be called on a Buffer that can represent 0 dimensions.");
1973 static_assert(
Dims ==
AnyDims ||
Dims == 0,
"make_scalar() must be called on a Buffer that can represent 0 dimensions.");
1981 static_assert(
Dims ==
AnyDims ||
Dims == 0,
"make_scalar() must be called on a Buffer that can represent 0 dimensions.");
1989 template<
typename T2,
int D2,
int S2>
1992 void (*deallocate_fn)(
void *) =
nullptr) {
2004 void (*deallocate_fn)(
void *)) {
2006 std::vector<int>
swaps;
2007 for (
int i = dimensions - 1;
i > 0;
i--) {
2008 for (
int j =
i;
j > 0;
j--) {
2009 if (shape[
j - 1].stride > shape[
j].stride) {
2010 std::swap(shape[
j - 1], shape[
j]);
2018 for (
int i = 0;
i < dimensions;
i++) {
2027 while (!
swaps.empty()) {
2029 std::swap(shape[
j - 1], shape[
j]);
2041 template<
typename...
Args>
2044 offset_of(
int d,
int first,
Args... rest)
const {
2045#if HALIDE_RUNTIME_BUFFER_CHECK_INDICES
2057 template<
typename...
Args>
2060 address_of(
Args... args)
const {
2062 return (storage_T *)(this->buf.
host) + offset_of(0, args...) * type().bytes();
2064 return (storage_T *)(this->buf.
host) + offset_of(0, args...);
2071 for (
int i = this->dimensions() - 1;
i >= 0;
i--) {
2072#if HALIDE_RUNTIME_BUFFER_CHECK_INDICES
2082 storage_T *address_of(
const int *
pos)
const {
2084 return (storage_T *)this->buf.
host + offset_of(
pos) * type().bytes();
2086 return (storage_T *)this->buf.
host + offset_of(
pos);
2093 return (T *)(this->buf.
host);
2103 template<
typename...
Args,
2104 typename =
typename std::enable_if<
AllInts<
Args...>::value>::type>
2106 static_assert(!T_is_void,
2107 "Cannot use operator() on Buffer<void> types");
2109 static_assert(
Dims ==
AnyDims ||
Dims ==
expected_dims,
"Buffer with static dimensions was accessed with the wrong number of coordinates in operator()");
2111 return *((
const not_void_T *)(address_of(first, rest...)));
2117 static_assert(!T_is_void,
2118 "Cannot use operator() on Buffer<void> types");
2120 static_assert(
Dims ==
AnyDims ||
Dims ==
expected_dims,
"Buffer with static dimensions was accessed with the wrong number of coordinates in operator()");
2122 return *((
const not_void_T *)(data()));
2128 static_assert(!T_is_void,
2129 "Cannot use operator() on Buffer<void> types");
2131 return *((
const not_void_T *)(address_of(
pos)));
2134 template<
typename...
Args,
2135 typename =
typename std::enable_if<
AllInts<
Args...>::value>::type>
2139 static_assert(!T_is_void,
2140 "Cannot use operator() on Buffer<void> types");
2142 static_assert(
Dims ==
AnyDims ||
Dims ==
expected_dims,
"Buffer with static dimensions was accessed with the wrong number of coordinates in operator()");
2144 return *((not_void_T *)(address_of(first, rest...)));
2150 static_assert(!T_is_void,
2151 "Cannot use operator() on Buffer<void> types");
2153 static_assert(
Dims ==
AnyDims ||
Dims ==
expected_dims,
"Buffer with static dimensions was accessed with the wrong number of coordinates in operator()");
2155 return *((not_void_T *)(data()));
2161 static_assert(!T_is_void,
2162 "Cannot use operator() on Buffer<void> types");
2164 return *((not_void_T *)(address_of(
pos)));
2170 bool all_equal =
true;
2171 for_each_element([&](
const int *
pos) { all_equal &= (*this)(
pos) == val; });
2177 for_each_value([=](T &v) { v = val; });
2185 struct for_each_value_task_dim {
2186 std::ptrdiff_t extent;
2187 std::ptrdiff_t stride[
N];
2193 template<
typename Ptr,
typename...
Ptrs>
2196 advance_ptrs(stride + 1,
ptrs...);
2200 static void advance_ptrs(
const std::ptrdiff_t *) {
2203 template<
typename Fn,
typename Ptr,
typename...
Ptrs>
2205 const for_each_value_task_dim<
sizeof...(
Ptrs) + 1> *t,
Ptr ptr,
Ptrs...
ptrs) {
2208 Ptr end = ptr + t[0].extent;
2209 while (ptr != end) {
2210 f(*ptr++, (*
ptrs++)...);
2213 for (std::ptrdiff_t
i = t[0].extent;
i != 0;
i--) {
2214 f(*ptr, (*ptrs)...);
2215 advance_ptrs(t[0].stride, ptr,
ptrs...);
2219 for (std::ptrdiff_t
i = t[d].extent;
i != 0;
i--) {
2221 advance_ptrs(t[d].stride, ptr,
ptrs...);
2230 const int dimensions = buffers[0]->
dimensions;
2234 for (
int i = 0;
i <
N;
i++) {
2235 if (buffers[
i]->device) {
2237 "Buffer passed to for_each_value has device allocation but no host allocation. Call allocate() and copy_to_host() first");
2238 assert(!buffers[
i]->device_dirty() &&
2239 "Buffer passed to for_each_value is dirty on device. Call copy_to_host() first");
2242 "Buffer passed to for_each_value has no host or device allocation");
2247 for (
int i = 0;
i < dimensions;
i++) {
2248 for (
int j = 0;
j <
N;
j++) {
2249 assert(buffers[
j]->dimensions == dimensions);
2250 assert(buffers[
j]->dim[
i].extent == buffers[0]->dim[
i].extent &&
2251 buffers[
j]->dim[
i].min == buffers[0]->dim[
i].min);
2260 for (
int j =
i;
j > 0 && t[
j].stride[
N - 1] < t[
j - 1].stride[
N - 1];
j--) {
2261 std::swap(t[
j], t[
j - 1]);
2268 for (
int i = 1;
i < d;
i++) {
2270 for (
int j = 0;
j <
N;
j++) {
2271 flat =
flat && t[
i - 1].stride[
j] * t[
i - 1].extent == t[
i].stride[
j];
2274 t[
i - 1].extent *= t[
i].extent;
2275 for (
int j =
i;
j < d - 1;
j++) {
2287 for (
int i = 0;
i <
N;
i++) {
2294 template<
typename Fn,
typename...
Args,
int N =
sizeof...(Args) + 1>
2296 if (dimensions() > 0) {
2335 template<
typename Fn,
typename...
Args,
int N =
sizeof...(Args) + 1>
2337 for_each_value_impl(f, std::forward<Args>(
other_buffers)...);
2341 template<
typename Fn,
typename...
Args,
int N =
sizeof...(Args) + 1>
2345 for_each_value_impl(f, std::forward<Args>(
other_buffers)...);
2352 struct for_each_element_task_dim {
2359 template<
typename Fn,
2361 typename =
decltype(std::declval<Fn>()(std::declval<Args>()...))>
2362 HALIDE_ALWAYS_INLINE static void for_each_element_variadic(
int,
int,
const for_each_element_task_dim *,
Fn &&f,
Args... args) {
2368 template<
typename Fn,
2370 HALIDE_ALWAYS_INLINE static void for_each_element_variadic(
double,
int d,
const for_each_element_task_dim *t,
Fn &&f,
Args... args) {
2371 for (
int i = t[d].min;
i <= t[d].
max;
i++) {
2372 for_each_element_variadic(0, d - 1, t, std::forward<Fn>(f),
i, args...);
2378 template<
typename Fn,
2380 typename =
decltype(std::declval<Fn>()(std::declval<Args>()...))>
2382 return (
int)(
sizeof...(Args));
2388 template<
typename Fn,
2391 static_assert(
sizeof...(args) <= 256,
2392 "Callable passed to for_each_element must accept either a const int *,"
2393 " or up to 256 ints. No such operator found. Expect infinite template recursion.");
2394 return num_args(0, std::forward<Fn>(f), 0, args...);
2404 typename =
typename std::enable_if<(d >= 0)>::type>
2405 HALIDE_ALWAYS_INLINE static void for_each_element_array_helper(
int,
const for_each_element_task_dim *t,
Fn &&f,
int *
pos) {
2406 for (
pos[d] = t[d].min;
pos[d] <= t[d].
max;
pos[d]++) {
2407 for_each_element_array_helper<d - 1>(0, t, std::forward<Fn>(f),
pos);
2414 typename =
typename std::enable_if<(d < 0)>::type>
2415 HALIDE_ALWAYS_INLINE static void for_each_element_array_helper(
double,
const for_each_element_task_dim *t,
Fn &&f,
int *
pos) {
2424 template<
typename Fn>
2425 static void for_each_element_array(
int d,
const for_each_element_task_dim *t,
Fn &&f,
int *
pos) {
2428 }
else if (d == 0) {
2433 }
else if (d == 1) {
2435 }
else if (d == 2) {
2437 }
else if (d == 3) {
2440 for (
pos[d] = t[d].min;
pos[d] <= t[d].
max;
pos[d]++) {
2441 for_each_element_array(d - 1, t, std::forward<Fn>(f),
pos);
2449 template<
typename Fn,
2450 typename =
decltype(std::declval<Fn>()((
const int *)
nullptr))>
2451 static void for_each_element(
int,
int dims,
const for_each_element_task_dim *t,
Fn &&f,
int check = 0) {
2452 const int size = dims *
sizeof(
int);
2457 for_each_element_array(dims - 1, t, std::forward<Fn>(f),
pos);
2462 template<
typename Fn>
2463 HALIDE_ALWAYS_INLINE static void for_each_element(
double,
int dims,
const for_each_element_task_dim *t,
Fn &&f) {
2464 int args = num_args(0, std::forward<Fn>(f));
2466 for_each_element_variadic(0, args - 1, t, std::forward<Fn>(f));
2469 template<
typename Fn>
2470 void for_each_element_impl(
Fn &&f)
const {
2471 for_each_element_task_dim *t =
2472 (for_each_element_task_dim *)
HALIDE_ALLOCA(dimensions() *
sizeof(for_each_element_task_dim));
2473 for (
int i = 0;
i < dimensions();
i++) {
2474 t[
i].min = dim(
i).min();
2475 t[
i].max = dim(
i).max();
2477 for_each_element(0, dimensions(), t, std::forward<Fn>(f));
2538 template<
typename Fn>
2540 for_each_element_impl(f);
2544 template<
typename Fn>
2548 for_each_element_impl(f);
2554 template<
typename Fn>
2559 template<
typename...
Args,
2560 typename =
decltype(std::declval<Fn>()(std::declval<Args>()...))>
2561 void operator()(
Args... args) {
2562 (*buf)(args...) = f(args...);
2575 template<
typename Fn,
2576 typename =
typename std::enable_if<!std::is_arithmetic<typename std::decay<Fn>::type>::value>::type>
2580 return for_each_element(
wrapper);
2588 return buf.is_bounds_query();
2597#if defined(__has_feature)
2598#if __has_feature(memory_sanitizer)
#define HALIDE_RUNTIME_BUFFER_ALLOCATION_ALIGNMENT
This file declares the routines used by Halide internally in its runtime.
#define HALIDE_NEVER_INLINE
@ halide_error_code_success
There was no error.
#define HALIDE_ALWAYS_INLINE
Read-only access to the shape.
HALIDE_ALWAYS_INLINE int min() const
The lowest coordinate in this dimension.
Dimension(const halide_dimension_t &dim)
HALIDE_ALWAYS_INLINE int max() const
The highest coordinate in this dimension.
HALIDE_ALWAYS_INLINE iterator end() const
An iterator that points to one past the max coordinate.
HALIDE_ALWAYS_INLINE int stride() const
The number of elements in memory you have to step over to increment this coordinate by one.
HALIDE_ALWAYS_INLINE iterator begin() const
An iterator that points to the min coordinate.
HALIDE_ALWAYS_INLINE int extent() const
The extent of the image along this dimension.
A templated Buffer class that wraps halide_buffer_t and adds functionality.
Buffer< T, Dims, InClassDimStorage > & operator=(const Buffer< T2, D2, S2 > &other)
Assign from another Buffer of possibly-different dimensionality and type.
Buffer< not_const_T, Dims, InClassDimStorage > copy_to_planar(void *(*allocate_fn)(size_t)=nullptr, void(*deallocate_fn)(void *)=nullptr) const
Like copy(), but the copy is created in planar memory layout (vs.
Buffer< T, Dims, InClassDimStorage > transposed(const std::vector< int > &order) const
Make a buffer which refers to the same data in the same layout using a different ordering of the dime...
void translate(int d, int delta)
Translate an image in-place along one dimension by changing how it is indexed.
Buffer(const halide_buffer_t &buf, BufferDeviceOwnership ownership=BufferDeviceOwnership::Unmanaged)
Make a Buffer from a halide_buffer_t.
void allocate(void *(*allocate_fn)(size_t)=nullptr, void(*deallocate_fn)(void *)=nullptr)
Allocate memory for this Buffer.
Buffer< not_const_T, Dims, InClassDimStorage > copy(void *(*allocate_fn)(size_t)=nullptr, void(*deallocate_fn)(void *)=nullptr) const
Make a new image which is a deep copy of this image.
Buffer< T,(Dims==AnyDims ? AnyDims :Dims+1)> embedded(int d, int pos=0) const
Make a new buffer that views this buffer as a single slice in a higher-dimensional space.
void add_dimension()
Add a new dimension with a min of zero and an extent of one.
void slice(int d)
Slice a buffer in-place at the dimension's minimum.
static void set_default_allocate_fn(void *(*allocate_fn)(size_t))
bool owns_host_memory() const
Does this Buffer own the host memory it refers to?
int width() const
Conventional names for the first three dimensions.
void transpose(const std::vector< int > &order)
A generalized transpose: instead of swapping two dimensions, pass a vector that lists each dimension ...
void set_min(const std::vector< int > &mins)
Set the min coordinate of an image in the first N dimensions.
HALIDE_ALWAYS_INLINE Buffer< T, Dims, InClassDimStorage > & for_each_element(Fn &&f)
Buffer(halide_type_t t, add_const_if_T_is_const< void > *data, const std::vector< int > &sizes)
Initialize an Buffer of runtime type from a pointer and a vector of sizes.
HALIDE_ALWAYS_INLINE Buffer< T2, D2, InClassDimStorage > as() &&
Return an rval reference to this Buffer.
int copy_to_host(void *ctx=nullptr)
Buffer(halide_type_t t, const std::vector< int > &sizes)
Allocate a new image of unknown type using a vector of ints as the size.
int device_malloc(const struct halide_device_interface_t *device_interface, void *ctx=nullptr)
int device_free(void *ctx=nullptr)
bool contains(Args... args) const
void crop(const std::vector< std::pair< int, int > > &rect)
Crop an image in-place along the first N dimensions.
HALIDE_ALWAYS_INLINE const Buffer< typename std::add_const< T >::type, Dims, InClassDimStorage > & as_const() const &
void set_device_dirty(bool v=true)
HALIDE_ALWAYS_INLINE const not_void_T & operator()(const int *pos) const
Buffer(T *data, int d, const halide_dimension_t *shape)
Initialize an Buffer from a pointer to the min coordinate and an array describing the shape.
Buffer(Buffer< T2, D2, S2 > &&other)
Move-construct a Buffer from a Buffer of different dimensionality and type.
void slice(int d, int pos)
Rewrite the buffer to refer to a single lower-dimensional slice of itself along the given dimension a...
HALIDE_ALWAYS_INLINE const not_void_T & operator()(int first, Args... rest) const
Access elements.
HALIDE_ALWAYS_INLINE void set_host_dirty(bool v=true)
Methods for managing any GPU allocation.
void msan_check_mem_is_initialized(bool entire=false) const
Convenient check to verify that all of the interesting bytes in the Buffer are initialized under MSAN...
HALIDE_ALWAYS_INLINE Buffer< typename std::add_const< T >::type, Dims, InClassDimStorage > as_const() &&
Buffer< T, Dims, InClassDimStorage > & operator=(Buffer< T, Dims, InClassDimStorage > &&other) noexcept
Standard move-assignment operator.
int device_detach_native(void *ctx=nullptr)
int device_wrap_native(const struct halide_device_interface_t *device_interface, uint64_t handle, void *ctx=nullptr)
Buffer< T, Dims, InClassDimStorage > translated(const std::vector< int > &delta) const
Make an image which refers to the same data translated along the first N dimensions.
HALIDE_ALWAYS_INLINE Dimension dim(int i) const
Access the shape of the buffer.
Buffer(int first, int second, Args... rest)
HALIDE_ALWAYS_INLINE Buffer< typename std::add_const< T >::type, Dims, InClassDimStorage > & as_const() &
as_const() is syntactic sugar for .as<const T>(), to avoid the need to recapitulate the type argument...
Buffer< T, Dims, InClassDimStorage > transposed(int d1, int d2) const
Make a buffer which refers to the same data in the same layout using a swapped indexing order for the...
HALIDE_ALWAYS_INLINE Buffer< T, Dims, InClassDimStorage > & for_each_value(Fn &&f, Args &&...other_buffers)
HALIDE_ALWAYS_INLINE not_void_T & operator()()
BufferDeviceOwnership device_ownership() const
Return the method by which the device field is managed.
void check_overflow()
Check the product of the extents fits in memory.
static bool can_convert_from(const Buffer< T2, D2, S2 > &other)
Determine if a Buffer<T, Dims, InClassDimStorage> can be constructed from some other Buffer type.
Buffer< not_const_T, Dims, InClassDimStorage > copy_to_interleaved(void *(*allocate_fn)(size_t)=nullptr, void(*deallocate_fn)(void *)=nullptr) const
Like copy(), but the copy is created in interleaved memory layout (vs.
int device_and_host_malloc(const struct halide_device_interface_t *device_interface, void *ctx=nullptr)
int device_sync(void *ctx=nullptr)
static Buffer< void, Dims, InClassDimStorage > make_interleaved(halide_type_t t, int width, int height, int channels)
If you use the (x, y, c) indexing convention, then Halide Buffers are stored planar by default.
Buffer(const std::vector< int > &sizes)
Allocate a new image of known type using a vector of ints as the size.
void embed(int d, int pos=0)
Embed a buffer in-place, increasing the dimensionality.
static constexpr halide_type_t static_halide_type()
Get the Halide type of T.
Buffer(T *data, int first, Args &&...rest)
Initialize an Buffer from a pointer and some sizes.
int copy_to_device(const struct halide_device_interface_t *device_interface, void *ctx=nullptr)
Buffer(Array(&vals)[N])
Make an Buffer that refers to a statically sized array.
const halide_buffer_t * raw_buffer() const
HALIDE_ALWAYS_INLINE not_void_T & operator()(int first, Args... rest)
static Buffer< T, Dims, InClassDimStorage > make_interleaved(int width, int height, int channels)
If you use the (x, y, c) indexing convention, then Halide Buffers are stored planar by default.
halide_type_t type() const
Get the type of the elements.
int device_and_host_free(const struct halide_device_interface_t *device_interface, void *ctx=nullptr)
Buffer(int first)
Allocate a new image of the given size.
halide_buffer_t * raw_buffer()
Get a pointer to the raw halide_buffer_t this wraps.
T * end() const
A pointer to one beyond the element with the highest address.
HALIDE_ALWAYS_INLINE bool device_dirty() const
Buffer< T, Dims, InClassDimStorage > cropped(const std::vector< std::pair< int, int > > &rect) const
Make an image that refers to a sub-rectangle of this image along the first N dimensions.
static constexpr int static_dimensions()
Callers should not use the result if has_static_dimensions is false.
void transpose(int d1, int d2)
Transpose a buffer in-place by changing how it is indexed.
void deallocate()
Drop reference to any owned host or device memory, possibly freeing it, if this buffer held the last ...
size_t size_in_bytes() const
The total number of bytes spanned by the data in memory.
bool has_device_allocation() const
void reset()
Reset the Buffer to be equivalent to a default-constructed Buffer of the same static type (if any); B...
Buffer(halide_type_t t, int first, Args... rest)
Allocate a new image of the given size with a runtime type.
int dimensions() const
Get the dimensionality of the buffer.
Buffer(halide_type_t t, add_const_if_T_is_const< void > *data, int d, const halide_dimension_t *shape)
Initialize an Buffer from a pointer to the min coordinate and an array describing the shape.
int min(int i) const
Access to the mins, strides, extents.
HALIDE_ALWAYS_INLINE const Buffer< T, Dims, InClassDimStorage > & for_each_element(Fn &&f) const
Call a function at each site in a buffer.
void device_deallocate()
Drop reference to any owned device memory, possibly freeing it if this buffer held the last reference...
HALIDE_ALWAYS_INLINE const not_void_T & operator()() const
static Buffer< T, Dims, InClassDimStorage > make_scalar()
Make a zero-dimensional Buffer.
void add_dimension_with_stride(int s)
Add a new dimension with a min of zero, an extent of one, and the specified stride.
Buffer(Buffer< T, Dims, InClassDimStorage > &&other) noexcept
Move constructor.
Buffer< T, Dims, InClassDimStorage > cropped(int d, int min, int extent) const
Make an image that refers to a sub-range of this image along the given dimension.
void crop(int d, int min, int extent)
Crop an image in-place along the given dimension.
Buffer< T, Dims, InClassDimStorage > & fill(Fn &&f)
Fill a buffer by evaluating a callable at every site.
static Buffer< T, Dims, InClassDimStorage > make_scalar(T *data)
Make a zero-dimensional Buffer that points to non-owned, existing data.
Buffer< T, Dims, InClassDimStorage > alias() const
Make a copy of the Buffer which shares the underlying host and/or device allocations as the existing ...
void set_min(Args... args)
size_t number_of_elements() const
The total number of elements this buffer represents.
static void assert_can_convert_from(const Buffer< T2, D2, S2 > &other)
Fail an assertion at runtime or compile-time if an Buffer<T, Dims, InClassDimStorage> cannot be const...
void translate(const std::vector< int > &delta)
Translate an image along the first N dimensions by changing how it is indexed.
Buffer(const Buffer< T, Dims, InClassDimStorage > &other)
Copy constructor.
HALIDE_ALWAYS_INLINE not_void_T & operator()(const int *pos)
T * data() const
Get a pointer to the address of the min coordinate.
Buffer< T, Dims, InClassDimStorage > & fill(not_void_T val)
Buffer(const std::vector< int > &sizes, const std::vector< int > &storage_order)
Buffer< T, Dims, InClassDimStorage > & operator=(Buffer< T2, D2, S2 > &&other)
Move from another Buffer of possibly-different dimensionality and type.
Buffer(halide_type_t t, const std::vector< int > &sizes, const std::vector< int > &storage_order)
Allocate a new image of unknown type using a vector of ints as the size and a vector of indices indic...
Buffer(halide_type_t t, add_const_if_T_is_const< void > *data, const std::vector< halide_dimension_t > &shape)
Initialize a Buffer from a pointer to the min coordinate and a vector describing the shape.
Buffer< T,(Dims==AnyDims ? AnyDims :Dims - 1)> sliced(int d, int pos) const
Make a lower-dimensional buffer that refers to one slice of this buffer.
static Buffer< add_const_if_T_is_const< void >, Dims, InClassDimStorage > make_interleaved(halide_type_t t, T *data, int width, int height, int channels)
Wrap an existing interleaved image.
HALIDE_ALWAYS_INLINE const Buffer< T, Dims, InClassDimStorage > & for_each_value(Fn &&f, Args &&...other_buffers) const
Call a function on every value in the buffer, and the corresponding values in some number of other bu...
bool is_bounds_query() const
Check if an input buffer passed extern stage is a querying bounds.
Buffer< T,(Dims==AnyDims ? AnyDims :Dims - 1)> sliced(int d) const
Make a lower-dimensional buffer that refers to one slice of this buffer at the dimension's minimum.
int left() const
Conventional names for the min and max value of each dimension.
void copy_from(Buffer< T2, D2, S2 > src)
Fill a Buffer with the values at the same coordinates in another Buffer.
Buffer< T, Dims, InClassDimStorage > translated(int d, int dx) const
Make an image which refers to the same data with using translated coordinates in the given dimension.
static Buffer< T, Dims, InClassDimStorage > make_interleaved(T *data, int width, int height, int channels)
Wrap an existing interleaved image.
static void set_default_deallocate_fn(void(*deallocate_fn)(void *))
static Buffer< T, Dims, InClassDimStorage > make_with_shape_of(Buffer< T2, D2, S2 > src, void *(*allocate_fn)(size_t)=nullptr, void(*deallocate_fn)(void *)=nullptr)
Make a buffer with the same shape and memory nesting order as another buffer.
Buffer(const Buffer< T2, D2, S2 > &other)
Construct a Buffer from a Buffer of different dimensionality and type.
bool contains(const std::vector< int > &coords) const
Test if a given coordinate is within the bounds of an image.
Buffer(T *data, const std::vector< halide_dimension_t > &shape)
Initialize a Buffer from a pointer to the min coordinate and a vector describing the shape.
Buffer(T *data, const std::vector< int > &sizes)
Initialize an Buffer from a pointer and a vector of sizes.
Buffer< T, Dims, InClassDimStorage > & operator=(const Buffer< T, Dims, InClassDimStorage > &other)
Standard assignment operator.
T * begin() const
A pointer to the element with the lowest address.
bool all_equal(not_void_T val) const
Tests that all values in this buffer are equal to val.
Buffer(halide_type_t t, add_const_if_T_is_const< void > *data, int first, Args &&...rest)
Initialize an Buffer of runtime type from a pointer and some sizes.
HALIDE_ALWAYS_INLINE Buffer< T2, D2, InClassDimStorage > & as() &
Return a typed reference to this Buffer.
HALIDE_ALWAYS_INLINE const Buffer< T2, D2, InClassDimStorage > & as() const &
Return a const typed reference to this Buffer.
static Buffer< add_const_if_T_is_const< void >, Dims, InClassDimStorage > make_scalar(halide_type_t t)
Make a zero-dimensional Buffer.
ConstantInterval min(const ConstantInterval &a, const ConstantInterval &b)
ConstantInterval max(const ConstantInterval &a, const ConstantInterval &b)
bool any_zero(const Container &c)
BufferDeviceOwnership
This indicates how to deallocate the device for a Halide::Runtime::Buffer.
@ AllocatedDeviceAndHost
No free routine will be called when device ref count goes to zero
@ WrappedNative
halide_device_free will be called when device ref count goes to zero
@ Unmanaged
halide_device_detach_native will be called when device ref count goes to zero
@ Cropped
Call device_and_host_free when DevRefCount goes to zero.
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
@ Internal
Not visible externally, similar to 'static' linkage in C.
Internal::ConstantInterval cast(Type t, const Internal::ConstantInterval &a)
Cast operators for ConstantIntervals.
unsigned __INT64_TYPE__ uint64_t
__UINTPTR_TYPE__ uintptr_t
ALWAYS_INLINE T align_up(T p, size_t alignment)
unsigned __INT8_TYPE__ uint8_t
__PTRDIFF_TYPE__ ptrdiff_t
unsigned __INT16_TYPE__ uint16_t
void * memcpy(void *s1, const void *s2, size_t n)
void * memset(void *s, int val, size_t n)
unsigned __INT32_TYPE__ uint32_t
int64_t min
The lower and upper bound of the interval.
An iterator class, so that you can iterate over coordinates in a dimensions using a range-based for l...
bool operator!=(const iterator &other) const
A similar struct for managing device allocations.
BufferDeviceOwnership ownership
static void *(* default_allocate_fn)(size_t)
static void(* default_deallocate_fn)(void *)
The raw representation of an image passed around by generated Halide code.
int32_t dimensions
The dimensionality of the buffer.
halide_dimension_t * dim
The shape of the buffer.
uint64_t device
A device-handle for e.g.
uint8_t * host
A pointer to the start of the data in main memory.
struct halide_type_t type
The type of each buffer element.
const struct halide_device_interface_t * device_interface
The interface used to interpret the above handle.
Each GPU API provides a halide_device_interface_t struct pointing to the code that manages device all...
int(* device_slice)(void *user_context, const struct halide_buffer_t *src, int slice_dim, int slice_pos, struct halide_buffer_t *dst)
int(* device_and_host_malloc)(void *user_context, struct halide_buffer_t *buf, const struct halide_device_interface_t *device_interface)
int(* wrap_native)(void *user_context, struct halide_buffer_t *buf, uint64_t handle, const struct halide_device_interface_t *device_interface)
int(* device_release_crop)(void *user_context, struct halide_buffer_t *buf)
int(* device_crop)(void *user_context, const struct halide_buffer_t *src, struct halide_buffer_t *dst)
int(* copy_to_host)(void *user_context, struct halide_buffer_t *buf)
int(* copy_to_device)(void *user_context, struct halide_buffer_t *buf, const struct halide_device_interface_t *device_interface)
int(* device_free)(void *user_context, struct halide_buffer_t *buf)
int(* detach_native)(void *user_context, struct halide_buffer_t *buf)
int(* device_and_host_free)(void *user_context, struct halide_buffer_t *buf)
int(* device_malloc)(void *user_context, struct halide_buffer_t *buf, const struct halide_device_interface_t *device_interface)
A runtime tag for a type in the halide type system.