• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
3 //
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
7 //
8 // See http://boostorg.github.com/compute for more information.
9 //---------------------------------------------------------------------------//
10 
11 #ifndef BOOST_COMPUTE_ALGORITHM_FILL_HPP
12 #define BOOST_COMPUTE_ALGORITHM_FILL_HPP
13 
14 #include <iterator>
15 
16 #include <boost/static_assert.hpp>
17 #include <boost/mpl/int.hpp>
18 #include <boost/mpl/vector.hpp>
19 #include <boost/mpl/contains.hpp>
20 #include <boost/utility/enable_if.hpp>
21 
22 #include <boost/compute/cl.hpp>
23 #include <boost/compute/system.hpp>
24 #include <boost/compute/command_queue.hpp>
25 #include <boost/compute/algorithm/copy.hpp>
26 #include <boost/compute/async/future.hpp>
27 #include <boost/compute/iterator/constant_iterator.hpp>
28 #include <boost/compute/iterator/discard_iterator.hpp>
29 #include <boost/compute/detail/is_buffer_iterator.hpp>
30 #include <boost/compute/detail/iterator_range_size.hpp>
31 #include <boost/compute/type_traits/is_device_iterator.hpp>
32 
33 
34 namespace boost {
35 namespace compute {
36 namespace detail {
37 
38 namespace mpl = boost::mpl;
39 
40 // fills the range [first, first + count) with value using copy()
41 template<class BufferIterator, class T>
fill_with_copy(BufferIterator first,size_t count,const T & value,command_queue & queue)42 inline void fill_with_copy(BufferIterator first,
43                            size_t count,
44                            const T &value,
45                            command_queue &queue)
46 {
47     ::boost::compute::copy(
48         ::boost::compute::make_constant_iterator(value, 0),
49         ::boost::compute::make_constant_iterator(value, count),
50         first,
51         queue
52     );
53 }
54 
55 // fills the range [first, first + count) with value using copy_async()
56 template<class BufferIterator, class T>
fill_async_with_copy(BufferIterator first,size_t count,const T & value,command_queue & queue)57 inline future<void> fill_async_with_copy(BufferIterator first,
58                                          size_t count,
59                                          const T &value,
60                                          command_queue &queue)
61 {
62     return ::boost::compute::copy_async(
63                ::boost::compute::make_constant_iterator(value, 0),
64                ::boost::compute::make_constant_iterator(value, count),
65                first,
66                queue
67            );
68 }
69 
70 #if defined(BOOST_COMPUTE_CL_VERSION_1_2)
71 
72 // meta-function returing true if Iterator points to a range of values
73 // that can be filled using clEnqueueFillBuffer(). to meet this criteria
74 // it must have a buffer accessible through iter.get_buffer() and the
75 // size of its value_type must by in {1, 2, 4, 8, 16, 32, 64, 128}.
76 template<class Iterator>
77 struct is_valid_fill_buffer_iterator :
78     public mpl::and_<
79         is_buffer_iterator<Iterator>,
80         mpl::contains<
81             mpl::vector<
82                 mpl::int_<1>,
83                 mpl::int_<2>,
84                 mpl::int_<4>,
85                 mpl::int_<8>,
86                 mpl::int_<16>,
87                 mpl::int_<32>,
88                 mpl::int_<64>,
89                 mpl::int_<128>
90             >,
91             mpl::int_<
92                 sizeof(typename std::iterator_traits<Iterator>::value_type)
93             >
94         >
95     >::type { };
96 
97 template<>
98 struct is_valid_fill_buffer_iterator<discard_iterator> : public boost::false_type {};
99 
100 // specialization which uses clEnqueueFillBuffer for buffer iterators
101 template<class BufferIterator, class T>
102 inline void
dispatch_fill(BufferIterator first,size_t count,const T & value,command_queue & queue,typename boost::enable_if<is_valid_fill_buffer_iterator<BufferIterator>>::type * =0)103 dispatch_fill(BufferIterator first,
104               size_t count,
105               const T &value,
106               command_queue &queue,
107               typename boost::enable_if<
108                  is_valid_fill_buffer_iterator<BufferIterator>
109               >::type* = 0)
110 {
111     typedef typename std::iterator_traits<BufferIterator>::value_type value_type;
112 
113     if(count == 0){
114         // nothing to do
115         return;
116     }
117 
118     // check if the device supports OpenCL 1.2 (required for enqueue_fill_buffer)
119     if(!queue.check_device_version(1, 2)){
120         return fill_with_copy(first, count, value, queue);
121     }
122 
123     value_type pattern = static_cast<value_type>(value);
124     size_t offset = static_cast<size_t>(first.get_index());
125 
126     if(count == 1){
127         // use clEnqueueWriteBuffer() directly when writing a single value
128         // to the device buffer. this is potentially more efficient and also
129         // works around a bug in the intel opencl driver.
130         queue.enqueue_write_buffer(
131             first.get_buffer(),
132             offset * sizeof(value_type),
133             sizeof(value_type),
134             &pattern
135         );
136     }
137     else {
138         queue.enqueue_fill_buffer(
139             first.get_buffer(),
140             &pattern,
141             sizeof(value_type),
142             offset * sizeof(value_type),
143             count * sizeof(value_type)
144         );
145     }
146 }
147 
148 template<class BufferIterator, class T>
149 inline future<void>
dispatch_fill_async(BufferIterator first,size_t count,const T & value,command_queue & queue,typename boost::enable_if<is_valid_fill_buffer_iterator<BufferIterator>>::type * =0)150 dispatch_fill_async(BufferIterator first,
151                     size_t count,
152                     const T &value,
153                     command_queue &queue,
154                     typename boost::enable_if<
155                        is_valid_fill_buffer_iterator<BufferIterator>
156                     >::type* = 0)
157 {
158     typedef typename std::iterator_traits<BufferIterator>::value_type value_type;
159 
160     // check if the device supports OpenCL 1.2 (required for enqueue_fill_buffer)
161     if(!queue.check_device_version(1, 2)){
162         return fill_async_with_copy(first, count, value, queue);
163     }
164 
165     value_type pattern = static_cast<value_type>(value);
166     size_t offset = static_cast<size_t>(first.get_index());
167 
168     event event_ =
169         queue.enqueue_fill_buffer(first.get_buffer(),
170                                   &pattern,
171                                   sizeof(value_type),
172                                   offset * sizeof(value_type),
173                                   count * sizeof(value_type));
174 
175     return future<void>(event_);
176 }
177 
178 #ifdef BOOST_COMPUTE_CL_VERSION_2_0
179 // specializations for svm_ptr<T>
180 template<class T>
dispatch_fill(svm_ptr<T> first,size_t count,const T & value,command_queue & queue)181 inline void dispatch_fill(svm_ptr<T> first,
182                           size_t count,
183                           const T &value,
184                           command_queue &queue)
185 {
186     if(count == 0){
187         return;
188     }
189 
190     queue.enqueue_svm_fill(
191         first.get(), &value, sizeof(T), count * sizeof(T)
192     );
193 }
194 
195 template<class T>
dispatch_fill_async(svm_ptr<T> first,size_t count,const T & value,command_queue & queue)196 inline future<void> dispatch_fill_async(svm_ptr<T> first,
197                                         size_t count,
198                                         const T &value,
199                                         command_queue &queue)
200 {
201     if(count == 0){
202         return future<void>();
203     }
204 
205     event event_ = queue.enqueue_svm_fill(
206         first.get(), &value, sizeof(T), count * sizeof(T)
207     );
208 
209     return future<void>(event_);
210 }
211 #endif // BOOST_COMPUTE_CL_VERSION_2_0
212 
213 // default implementations
214 template<class BufferIterator, class T>
215 inline void
dispatch_fill(BufferIterator first,size_t count,const T & value,command_queue & queue,typename boost::disable_if<is_valid_fill_buffer_iterator<BufferIterator>>::type * =0)216 dispatch_fill(BufferIterator first,
217               size_t count,
218               const T &value,
219               command_queue &queue,
220               typename boost::disable_if<
221                   is_valid_fill_buffer_iterator<BufferIterator>
222               >::type* = 0)
223 {
224     fill_with_copy(first, count, value, queue);
225 }
226 
227 template<class BufferIterator, class T>
228 inline future<void>
dispatch_fill_async(BufferIterator first,size_t count,const T & value,command_queue & queue,typename boost::disable_if<is_valid_fill_buffer_iterator<BufferIterator>>::type * =0)229 dispatch_fill_async(BufferIterator first,
230                     size_t count,
231                     const T &value,
232                     command_queue &queue,
233                     typename boost::disable_if<
234                         is_valid_fill_buffer_iterator<BufferIterator>
235                     >::type* = 0)
236 {
237     return fill_async_with_copy(first, count, value, queue);
238 }
239 #else
240 template<class BufferIterator, class T>
dispatch_fill(BufferIterator first,size_t count,const T & value,command_queue & queue)241 inline void dispatch_fill(BufferIterator first,
242                           size_t count,
243                           const T &value,
244                           command_queue &queue)
245 {
246     fill_with_copy(first, count, value, queue);
247 }
248 
249 template<class BufferIterator, class T>
dispatch_fill_async(BufferIterator first,size_t count,const T & value,command_queue & queue)250 inline future<void> dispatch_fill_async(BufferIterator first,
251                                         size_t count,
252                                         const T &value,
253                                         command_queue &queue)
254 {
255     return fill_async_with_copy(first, count, value, queue);
256 }
257 #endif // !defined(BOOST_COMPUTE_CL_VERSION_1_2)
258 
259 } // end detail namespace
260 
261 /// Fills the range [\p first, \p last) with \p value.
262 ///
263 /// \param first first element in the range to fill
264 /// \param last last element in the range to fill
265 /// \param value value to copy to each element
266 /// \param queue command queue to perform the operation
267 ///
268 /// For example, to fill a vector on the device with sevens:
269 /// \code
270 /// // vector on the device
271 /// boost::compute::vector<int> vec(10, context);
272 ///
273 /// // fill vector with sevens
274 /// boost::compute::fill(vec.begin(), vec.end(), 7, queue);
275 /// \endcode
276 ///
277 /// Space complexity: \Omega(1)
278 ///
279 /// \see boost::compute::fill_n()
280 template<class BufferIterator, class T>
fill(BufferIterator first,BufferIterator last,const T & value,command_queue & queue=system::default_queue ())281 inline void fill(BufferIterator first,
282                  BufferIterator last,
283                  const T &value,
284                  command_queue &queue = system::default_queue())
285 {
286     BOOST_STATIC_ASSERT(is_device_iterator<BufferIterator>::value);
287     size_t count = detail::iterator_range_size(first, last);
288     if(count == 0){
289         return;
290     }
291 
292     detail::dispatch_fill(first, count, value, queue);
293 }
294 
295 template<class BufferIterator, class T>
fill_async(BufferIterator first,BufferIterator last,const T & value,command_queue & queue=system::default_queue ())296 inline future<void> fill_async(BufferIterator first,
297                                BufferIterator last,
298                                const T &value,
299                                command_queue &queue = system::default_queue())
300 {
301     BOOST_STATIC_ASSERT(detail::is_buffer_iterator<BufferIterator>::value);
302     size_t count = detail::iterator_range_size(first, last);
303     if(count == 0){
304         return future<void>();
305     }
306 
307     return detail::dispatch_fill_async(first, count, value, queue);
308 }
309 
310 } // end compute namespace
311 } // end boost namespace
312 
313 #endif // BOOST_COMPUTE_ALGORITHM_FILL_HPP
314