From 1bb84de08fb19bc98d155490fb5d5b532e7ca71c Mon Sep 17 00:00:00 2001 From: Emanuel Rietveld Date: Wed, 14 Mar 2018 12:13:50 +0000 Subject: [PATCH] Add get_surfobj and get_texobj to cuda arrays --- src/cpp/cuda.hpp | 79 ++++++++ src/wrapper/wrap_cudadrv.cpp | 43 +++++ src/wrapper/wrap_helpers.hpp | 340 ++++++++++++++++++++++++++++++++++- 3 files changed, 461 insertions(+), 1 deletion(-) diff --git a/src/cpp/cuda.hpp b/src/cpp/cuda.hpp index 3dab50f0..ece725aa 100644 --- a/src/cpp/cuda.hpp +++ b/src/cpp/cuda.hpp @@ -1003,6 +1003,63 @@ namespace pycuda // }}} + // {{{ texture object +#if CUDAPP_CUDA_VERSION >= 5000 + class texture_object : public boost::noncopyable + { + private: + CUtexObject m_texobj; + bool m_managed; + + public: + texture_object(CUtexObject to, bool managed) + : m_texobj(to), m_managed(managed) + { } + + ~texture_object() + { + if (m_managed) + { + CUDAPP_CALL_GUARDED_CLEANUP(cuTexObjectDestroy, (m_texobj)); + } + } + + CUtexObject handle() const + { return m_texobj; } + }; +#endif + + // }}} + + // {{{ surface object +#if CUDAPP_CUDA_VERSION >= 5000 + class surface_object : public boost::noncopyable + { + private: + CUsurfObject m_surfobj; + bool m_managed; + + public: + surface_object(CUsurfObject so, bool managed) + : m_surfobj(so), m_managed(managed) + { } + + ~surface_object() + { + if (m_managed) + { + CUDAPP_CALL_GUARDED_CLEANUP(cuSurfObjectDestroy, (m_surfobj)); + } + } + + CUsurfObject handle() const + { return m_surfobj; } + }; +#endif + + // }}} + + // {{{ array class array : public boost::noncopyable, public context_dependent { @@ -1060,6 +1117,28 @@ namespace pycuda } #endif +#if CUDAPP_CUDA_VERSION >= 5000 + texture_object *get_texobj(CUDA_TEXTURE_DESC* pTexDesc) { + CUtexObject texObject; + CUDA_RESOURCE_DESC resDesc; + memset(&resDesc,0,sizeof(CUDA_RESOURCE_DESC)); + resDesc.resType = CU_RESOURCE_TYPE_ARRAY; + resDesc.res.array.hArray = m_array; + CUDAPP_CALL_GUARDED(cuTexObjectCreate, (&texObject, &resDesc, pTexDesc, NULL)); + return new texture_object(texObject, true); + } + + surface_object *get_surfobj() { + CUsurfObject surfObject; + CUDA_RESOURCE_DESC resDesc; + memset(&resDesc,0,sizeof(CUDA_RESOURCE_DESC)); + resDesc.resType = CU_RESOURCE_TYPE_ARRAY; + resDesc.res.array.hArray = m_array; + CUDAPP_CALL_GUARDED(cuSurfObjectCreate, (&surfObject, &resDesc)); + return new surface_object(surfObject, true); + } +#endif + CUarray handle() const { return m_array; } diff --git a/src/wrapper/wrap_cudadrv.cpp b/src/wrapper/wrap_cudadrv.cpp index 8a2488de..6c38bf14 100644 --- a/src/wrapper/wrap_cudadrv.cpp +++ b/src/wrapper/wrap_cudadrv.cpp @@ -1586,12 +1586,55 @@ BOOST_PYTHON_MODULE(_driver) #if CUDAPP_CUDA_VERSION >= 2000 .def(py::init()) .DEF_SIMPLE_METHOD(get_descriptor_3d) +#endif +#if CUDAPP_CUDA_VERSION >= 5000 + .def("get_texobj", &cl::get_texobj, + py::return_value_policy()) + .def("get_surfobj", &cl::get_surfobj, + py::return_value_policy()) #endif .add_property("handle", &cl::handle_int) ; } // }}} + // {{{ texture object +#if CUDAPP_CUDA_VERSION >= 5000 + { + typedef CUDA_TEXTURE_DESC cl; + py::class_("TextureDesc") + .add_property("address_mode", make_array(&cl::addressMode)) + .add_property("border_color", make_array(&cl::borderColor)) + .def_readwrite("filter_mode", &cl::filterMode) + .def_readwrite("flags", &cl::flags) + .def_readwrite("max_anisotropy", &cl::maxAnisotropy) + .def_readwrite("max_mipmap_level_clamp", &cl::maxMipmapLevelClamp) + .def_readwrite("min_mipmap_level_clamp", &cl::minMipmapLevelClamp) + .def_readwrite("mipmap_filter_mode", &cl::mipmapFilterMode) + .def_readwrite("mipmap_level_bias", &cl::mipmapLevelBias) + ; + } + + { + typedef texture_object cl; + py::class_("TextureObject", py::no_init) + .add_property("handle", &cl::handle) + ; + } +#endif + // }}} + + // {{{ surface object +#if CUDAPP_CUDA_VERSION >= 5000 + { + typedef surface_object cl; + py::class_("SurfaceObject", py::no_init) + .add_property("handle", &cl::handle) + ; + } +#endif + // }}} + // {{{ texture reference { typedef texture_reference cl; diff --git a/src/wrapper/wrap_helpers.hpp b/src/wrapper/wrap_helpers.hpp index 00e2f937..b41e7f30 100644 --- a/src/wrapper/wrap_helpers.hpp +++ b/src/wrapper/wrap_helpers.hpp @@ -7,7 +7,7 @@ #include #include #include - +#include @@ -57,6 +57,344 @@ namespace } } +namespace detail { + +template struct array_trait; + +/// @brief Type that proxies to an array. +template +class array_proxy +{ +public: + // Types + typedef T value_type; + typedef T* iterator; + typedef T& reference; + typedef std::size_t size_type; + + /// @brief Empty constructor. + array_proxy() + : ptr_(0), + length_(0) + {} + + /// @brief Construct with iterators. + template + array_proxy(Iterator begin, Iterator end) + : ptr_(&*begin), + length_(std::distance(begin, end)) + {} + + /// @brief Construct with with start and size. + array_proxy(reference begin, std::size_t length) + : ptr_(&begin), + length_(length) + {} + + // Iterator support. + iterator begin() { return ptr_; } + iterator end() { return ptr_ + length_; } + + // Element access. + reference operator[](size_t i) { return ptr_[i]; } + + // Capacity. + size_type size() { return length_; } + +private: + T* ptr_; + std::size_t length_; +}; + +/// @brief Make an array_proxy. +template +array_proxy::element_type> +make_array_proxy(T& array) +{ + return array_proxy::element_type>( + array[0], + array_trait::static_size); +} + +/// @brief Policy type for referenced indexing, meeting the DerivedPolicies +/// requirement of boost::python::index_suite. +/// +/// @note Requires Container to support: +/// - value_type and size_type types, +/// - value_type is default constructable and copyable, +/// - element access via operator[], +/// - Default constructable, iterator constructable, +/// - begin(), end(), and size() member functions +template +class ref_index_suite + : public boost::python::indexing_suite > +{ +public: + + typedef typename Container::value_type data_type; + typedef typename Container::size_type index_type; + typedef typename Container::size_type size_type; + + // Element access and manipulation. + + /// @brief Get element from container. + static data_type& + get_item(Container& container, index_type index) + { + return container[index]; + } + + /// @brief Set element from container. + static void + set_item(Container& container, index_type index, const data_type& value) + { + container[index] = value; + } + + /// @brief Reset index to default value. + static void + delete_item(Container& container, index_type index) + { + set_item(container, index, data_type()); + }; + + // Slice support. + + /// @brief Get slice from container. + /// + /// @return Python object containing + static boost::python::object + get_slice(Container& container, index_type from, index_type to) + { + using boost::python::list; + if (from > to) return list(); + + // Return copy, as container only references its elements. + list list; + while (from != to) list.append(container[from++]); + return list; + }; + + /// @brief Set a slice in container with a given value. + static void + set_slice( + Container& container, index_type from, + index_type to, const data_type& value + ) + { + // If range is invalid, return early. + if (from > to) return; + + // Populate range with value. + while (from < to) container[from++] = value; + } + + /// @brief Set a slice in container with another range. + template + static void + set_slice( + Container& container, index_type from, + index_type to, Iterator first, Iterator last + ) + { + // If range is invalid, return early. + if (from > to) return; + + // Populate range with other range. + while (from < to) container[from++] = *first++; + } + + /// @brief Reset slice to default values. + static void + delete_slice(Container& container, index_type from, index_type to) + { + set_slice(container, from, to, data_type()); + } + + // Capacity. + + /// @brief Get size of container. + static std::size_t + size(Container& container) { return container.size(); } + + /// @brief Check if a value is within the container. + template + static bool + contains(Container& container, const T& value) + { + return std::find(container.begin(), container.end(), value) + != container.end(); + } + + /// @brief Minimum index supported for container. + static index_type + get_min_index(Container& /*container*/) + { + return 0; + } + + /// @brief Maximum index supported for container. + static index_type + get_max_index(Container& container) + { + return size(container); + } + + // Misc. + + /// @brief Convert python index (could be negative) to a valid container + /// index with proper boundary checks. + static index_type + convert_index(Container& container, PyObject* object) + { + namespace python = boost::python; + python::extract py_index(object); + + // If py_index cannot extract a long, then type the type is wrong so + // set error and return early. + if (!py_index.check()) + { + PyErr_SetString(PyExc_TypeError, "Invalid index type"); + python::throw_error_already_set(); + return index_type(); + } + + // Extract index. + long index = py_index(); + + // Adjust negative index. + if (index < 0) + index += container.size(); + + // Boundary check. + if (index >= long(container.size()) || index < 0) + { + PyErr_SetString(PyExc_IndexError, "Index out of range"); + python::throw_error_already_set(); + } + + return index; + } +}; + +/// @brief Trait for arrays. +template +struct array_trait_impl; + +// Specialize for native array. +template +struct array_trait_impl +{ + typedef T element_type; + enum { static_size = N }; + typedef array_proxy proxy_type; + typedef boost::python::default_call_policies policy; + typedef boost::mpl::vector > signature; +}; + +// Specialize boost::array to use the native array trait. +template +struct array_trait_impl > + : public array_trait_impl +{}; + +// @brief Specialize for member objects to use and modify non member traits. +template +struct array_trait_impl + : public array_trait_impl +{ + typedef boost::python::with_custodian_and_ward_postcall< + 0, // return object (custodian) + 1 // self or this (ward) + > policy; + + // Append the class to the signature. + typedef typename boost::mpl::push_back< + typename array_trait_impl::signature, C&>::type signature; +}; + +/// @brief Trait class used to deduce array information, policies, and +/// signatures +template +struct array_trait: + public array_trait_impl::type> +{ + typedef T native_type; +}; + +/// @brief Functor used used convert an array to an array_proxy for +/// non-member objects. +template +struct array_proxy_getter +{ +public: + typedef typename Trait::native_type native_type; + typedef typename Trait::proxy_type proxy_type; + + /// @brief Constructor. + array_proxy_getter(native_type array): array_(array) {} + + /// @brief Return an array_proxy for a member array object. + template + proxy_type operator()(C& c) { return make_array_proxy(c.*array_); } + + /// @brief Return an array_proxy for a non-member array object. + proxy_type operator()() { return make_array_proxy(*array_); } +private: + native_type array_; +}; + +/// @brief Conditionally register a type with Boost.Python. +template +void register_array_proxy() +{ + typedef typename Trait::element_type element_type; + typedef typename Trait::proxy_type proxy_type; + + // If type is already registered, then return early. + namespace python = boost::python; + bool is_registered = (0 != python::converter::registry::query( + python::type_id())->to_python_target_type()); + if (is_registered) return; + + // Otherwise, register the type as an internal type. + std::string type_name = std::string("_") + typeid(element_type).name(); + python::class_(type_name.c_str(), python::no_init) + .def(ref_index_suite()); +} + +/// @brief Create a callable Boost.Python object that will return an +/// array_proxy type when called. +/// +/// @note This function will conditionally register array_proxy types +/// for conversion within Boost.Python. The array_proxy will +/// extend the life of the object from which it was called. +/// For example, if `foo` is an object, and `vars` is an array, +/// then the object returned from `foo.vars` will extend the life +/// of `foo`. +template +boost::python::object make_array_aux(Array array) +{ + typedef array_trait trait_type; + // Register an array proxy. + register_array_proxy(); + + // Create function. + return boost::python::make_function( + array_proxy_getter(array), + typename trait_type::policy(), + typename trait_type::signature()); +} + +} // namespace detail + +/// @brief Create a callable Boost.Python object from an array. +template +boost::python::object make_array(T array) +{ + return detail::make_array_aux(array); +}