| 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 | #define BOOST_TEST_MODULE TestCopy |
| 12 | #include <boost/test/unit_test.hpp> |
| 13 | |
| 14 | #include <list> |
| 15 | #include <vector> |
| 16 | #include <string> |
| 17 | #include <sstream> |
| 18 | #include <iterator> |
| 19 | #include <iostream> |
| 20 | |
| 21 | #include <boost/compute/svm.hpp> |
| 22 | #include <boost/compute/system.hpp> |
| 23 | #include <boost/compute/functional.hpp> |
| 24 | #include <boost/compute/command_queue.hpp> |
| 25 | #include <boost/compute/algorithm/copy.hpp> |
| 26 | #include <boost/compute/algorithm/copy_n.hpp> |
| 27 | #include <boost/compute/algorithm/fill.hpp> |
| 28 | #include <boost/compute/algorithm/iota.hpp> |
| 29 | #include <boost/compute/async/future.hpp> |
| 30 | #include <boost/compute/container/vector.hpp> |
| 31 | #include <boost/compute/detail/device_ptr.hpp> |
| 32 | #include <boost/compute/iterator/detail/swizzle_iterator.hpp> |
| 33 | |
| 34 | #include "quirks.hpp" |
| 35 | #include "check_macros.hpp" |
| 36 | #include "context_setup.hpp" |
| 37 | |
| 38 | namespace bc = boost::compute; |
| 39 | namespace compute = boost::compute; |
| 40 | |
| 41 | BOOST_AUTO_TEST_CASE(copy_on_device) |
| 42 | { |
| 43 | float data[] = { 6.1f, 10.2f, 19.3f, 25.4f }; |
| 44 | bc::vector<float> a(4, context); |
| 45 | bc::copy(data, data + 4, a.begin(), queue); |
| 46 | CHECK_RANGE_EQUAL(float, 4, a, (6.1f, 10.2f, 19.3f, 25.4f)); |
| 47 | |
| 48 | bc::vector<float> b(4, context); |
| 49 | bc::fill(first: b.begin(), last: b.end(), value: 0, queue); |
| 50 | CHECK_RANGE_EQUAL(float, 4, b, (0.0f, 0.0f, 0.0f, 0.0f)); |
| 51 | |
| 52 | bc::copy(a.begin(), a.end(), b.begin(), queue); |
| 53 | CHECK_RANGE_EQUAL(float, 4, b, (6.1f, 10.2f, 19.3f, 25.4f)); |
| 54 | |
| 55 | bc::vector<float> c(context); |
| 56 | bc::copy(first: c.begin(), last: c.end(), result: b.begin(), queue); |
| 57 | CHECK_RANGE_EQUAL(float, 4, b, (6.1f, 10.2f, 19.3f, 25.4f)); |
| 58 | } |
| 59 | |
| 60 | BOOST_AUTO_TEST_CASE(copy_on_device_device_ptr) |
| 61 | { |
| 62 | float data[] = { 6.1f, 10.2f, 19.3f, 25.4f }; |
| 63 | bc::vector<float> a(4, context); |
| 64 | bc::copy(first: data, last: data + 4, result: a.begin(), queue); |
| 65 | CHECK_RANGE_EQUAL(float, 4, a, (6.1f, 10.2f, 19.3f, 25.4f)); |
| 66 | |
| 67 | bc::vector<float> b(4, context); |
| 68 | bc::detail::device_ptr<float> b_ptr(b.get_buffer(), size_t(0)); |
| 69 | |
| 70 | // buffer_iterator -> device_ptr |
| 71 | bc::copy(first: a.begin(), last: a.end(), result: b_ptr, queue); |
| 72 | CHECK_RANGE_EQUAL(float, 4, b, (6.1f, 10.2f, 19.3f, 25.4f)); |
| 73 | |
| 74 | bc::vector<float> c(4, context); |
| 75 | bc::fill(first: c.begin(), last: c.end(), value: 0.0f, queue); |
| 76 | bc::detail::device_ptr<float> c_ptr(c.get_buffer(), size_t(2)); |
| 77 | |
| 78 | // device_ptr -> device_ptr |
| 79 | bc::copy(first: b_ptr, last: b_ptr + 2, result: c_ptr, queue); |
| 80 | CHECK_RANGE_EQUAL(float, 4, c, (0.0f, 0.0f, 6.1f, 10.2f)); |
| 81 | |
| 82 | // device_ptr -> buffer_iterator |
| 83 | bc::copy(c_ptr, c_ptr + 2, a.begin() + 2, queue); |
| 84 | CHECK_RANGE_EQUAL(float, 4, a, (6.1f, 10.2f, 6.1f, 10.2f)); |
| 85 | } |
| 86 | |
| 87 | BOOST_AUTO_TEST_CASE(copy_on_host) |
| 88 | { |
| 89 | int data[] = { 2, 4, 6, 8 }; |
| 90 | std::vector<int> vector(4); |
| 91 | compute::copy(first: data, last: data + 4, result: vector.begin(), queue); |
| 92 | CHECK_RANGE_EQUAL(int, 4, vector, (2, 4, 6, 8)); |
| 93 | } |
| 94 | |
| 95 | BOOST_AUTO_TEST_CASE(copy) |
| 96 | { |
| 97 | int data[] = { 1, 2, 5, 6 }; |
| 98 | bc::vector<int> vector(4, context); |
| 99 | bc::copy(data, data + 4, vector.begin(), queue); |
| 100 | CHECK_RANGE_EQUAL(int, 4, vector, (1, 2, 5, 6)); |
| 101 | |
| 102 | std::vector<int> host_vector(4); |
| 103 | bc::copy(vector.begin(), vector.end(), host_vector.begin(), queue); |
| 104 | BOOST_CHECK_EQUAL(host_vector[0], 1); |
| 105 | BOOST_CHECK_EQUAL(host_vector[1], 2); |
| 106 | BOOST_CHECK_EQUAL(host_vector[2], 5); |
| 107 | BOOST_CHECK_EQUAL(host_vector[3], 6); |
| 108 | } |
| 109 | |
| 110 | BOOST_AUTO_TEST_CASE(empty_copy) |
| 111 | { |
| 112 | int data[] = { 1, 2, 5, 6 }; |
| 113 | bc::vector<int> a(4, context); |
| 114 | bc::vector<int> b(context); |
| 115 | std::vector<int> c; |
| 116 | |
| 117 | bc::copy(first: data, last: data + 4, result: a.begin(), queue); |
| 118 | CHECK_RANGE_EQUAL(int, 4, a, (1, 2, 5, 6)); |
| 119 | |
| 120 | bc::copy(first: b.begin(), last: b.end(), result: a.begin(), queue); |
| 121 | CHECK_RANGE_EQUAL(int, 4, a, (1, 2, 5, 6)); |
| 122 | |
| 123 | bc::copy(first: c.begin(), last: c.end(), result: a.begin(), queue); |
| 124 | CHECK_RANGE_EQUAL(int, 4, a, (1, 2, 5, 6)); |
| 125 | |
| 126 | bc::future<bc::vector<int>::iterator> future = |
| 127 | bc::copy_async(c.begin(), c.end(), a.begin(), queue); |
| 128 | if(future.valid()) |
| 129 | future.wait(); |
| 130 | CHECK_RANGE_EQUAL(int, 4, a, (1, 2, 5, 6)); |
| 131 | } |
| 132 | |
| 133 | // Test copying from a std::list to a bc::vector. This differs from |
| 134 | // the test copying from std::vector because std::list has non-contigous |
| 135 | // storage for its data values. |
| 136 | BOOST_AUTO_TEST_CASE(copy_from_host_list) |
| 137 | { |
| 138 | int data[] = { -4, 12, 9, 0 }; |
| 139 | std::list<int> host_list(data, data + 4); |
| 140 | |
| 141 | bc::vector<int> vector(4, context); |
| 142 | bc::copy(first: host_list.begin(), last: host_list.end(), result: vector.begin(), queue); |
| 143 | CHECK_RANGE_EQUAL(int, 4, vector, (-4, 12, 9, 0)); |
| 144 | } |
| 145 | |
| 146 | BOOST_AUTO_TEST_CASE(copy_n_int) |
| 147 | { |
| 148 | int data[] = { 1, 2, 3, 4, 5 }; |
| 149 | bc::vector<int> a(data, data + 5, queue); |
| 150 | |
| 151 | bc::vector<int> b(5, context); |
| 152 | bc::fill(first: b.begin(), last: b.end(), value: 0, queue); |
| 153 | bc::copy_n(first: a.begin(), count: 3, result: b.begin(), queue); |
| 154 | CHECK_RANGE_EQUAL(int, 5, b, (1, 2, 3, 0, 0)); |
| 155 | |
| 156 | bc::copy_n(first: b.begin(), count: 4, result: a.begin(), queue); |
| 157 | CHECK_RANGE_EQUAL(int, 5, a, (1, 2, 3, 0, 5)); |
| 158 | } |
| 159 | |
| 160 | BOOST_AUTO_TEST_CASE(copy_swizzle_iterator) |
| 161 | { |
| 162 | using bc::int2_; |
| 163 | using bc::int4_; |
| 164 | |
| 165 | int data[] = { 1, 2, 3, 4, |
| 166 | 5, 6, 7, 8, |
| 167 | 9, 1, 2, 3, |
| 168 | 4, 5, 6, 7 }; |
| 169 | |
| 170 | bc::vector<int4_> input(reinterpret_cast<int4_*>(data), |
| 171 | reinterpret_cast<int4_*>(data) + 4, |
| 172 | queue); |
| 173 | BOOST_CHECK_EQUAL(input.size(), size_t(4)); |
| 174 | CHECK_RANGE_EQUAL(int4_, 4, input, |
| 175 | (int4_(1, 2, 3, 4), |
| 176 | int4_(5, 6, 7, 8), |
| 177 | int4_(9, 1, 2, 3), |
| 178 | int4_(4, 5, 6, 7)) |
| 179 | ); |
| 180 | |
| 181 | bc::vector<int4_> output4(4, context); |
| 182 | bc::copy( |
| 183 | bc::detail::make_swizzle_iterator<4>(input.begin(), "wzyx" ), |
| 184 | bc::detail::make_swizzle_iterator<4>(input.end(), "wzyx" ), |
| 185 | output4.begin(), |
| 186 | queue |
| 187 | ); |
| 188 | CHECK_RANGE_EQUAL(int4_, 4, output4, |
| 189 | (int4_(4, 3, 2, 1), |
| 190 | int4_(8, 7, 6, 5), |
| 191 | int4_(3, 2, 1, 9), |
| 192 | int4_(7, 6, 5, 4)) |
| 193 | ); |
| 194 | |
| 195 | bc::vector<int2_> output2(4, context); |
| 196 | bc::copy( |
| 197 | bc::detail::make_swizzle_iterator<2>(input.begin(), "xz" ), |
| 198 | bc::detail::make_swizzle_iterator<2>(input.end(), "xz" ), |
| 199 | output2.begin(), |
| 200 | queue |
| 201 | ); |
| 202 | CHECK_RANGE_EQUAL(int2_, 4, output2, |
| 203 | (int2_(1, 3), |
| 204 | int2_(5, 7), |
| 205 | int2_(9, 2), |
| 206 | int2_(4, 6)) |
| 207 | ); |
| 208 | |
| 209 | bc::vector<int> output1(4, context); |
| 210 | bc::copy( |
| 211 | bc::detail::make_swizzle_iterator<1>(input.begin(), "y" ), |
| 212 | bc::detail::make_swizzle_iterator<1>(input.end(), "y" ), |
| 213 | output1.begin(), |
| 214 | queue |
| 215 | ); |
| 216 | CHECK_RANGE_EQUAL(int, 4, output1, (2, 6, 1, 5)); |
| 217 | } |
| 218 | |
| 219 | BOOST_AUTO_TEST_CASE(copy_int_async) |
| 220 | { |
| 221 | // setup host data |
| 222 | int host_data[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; |
| 223 | typedef int* host_iterator; |
| 224 | |
| 225 | // setup device data |
| 226 | bc::vector<int> device_data(8, context); |
| 227 | typedef bc::vector<int>::iterator device_iterator; |
| 228 | |
| 229 | // copy data to device |
| 230 | bc::future<device_iterator> host_to_device_future = |
| 231 | bc::copy_async(first: host_data, last: host_data + 8, result: device_data.begin(), queue); |
| 232 | |
| 233 | // wait for copy to complete |
| 234 | host_to_device_future.wait(); |
| 235 | |
| 236 | // check results |
| 237 | CHECK_RANGE_EQUAL(int, 8, device_data, (1, 2, 3, 4, 5, 6, 7, 8)); |
| 238 | BOOST_VERIFY(host_to_device_future.get() == device_data.end()); |
| 239 | |
| 240 | // fill host data with zeros |
| 241 | std::fill(first: host_data, last: host_data + 8, value: int(0)); |
| 242 | |
| 243 | // copy data back to host |
| 244 | bc::future<host_iterator> device_to_host_future = |
| 245 | bc::copy_async(first: device_data.begin(), last: device_data.end(), result: host_data, queue); |
| 246 | |
| 247 | // wait for copy to complete |
| 248 | device_to_host_future.wait(); |
| 249 | |
| 250 | // check results |
| 251 | BOOST_CHECK_EQUAL(host_data[0], int(1)); |
| 252 | BOOST_CHECK_EQUAL(host_data[1], int(2)); |
| 253 | BOOST_CHECK_EQUAL(host_data[2], int(3)); |
| 254 | BOOST_CHECK_EQUAL(host_data[3], int(4)); |
| 255 | BOOST_CHECK_EQUAL(host_data[4], int(5)); |
| 256 | BOOST_CHECK_EQUAL(host_data[5], int(6)); |
| 257 | BOOST_CHECK_EQUAL(host_data[6], int(7)); |
| 258 | BOOST_CHECK_EQUAL(host_data[7], int(8)); |
| 259 | BOOST_VERIFY(device_to_host_future.get() == host_data + 8); |
| 260 | } |
| 261 | |
| 262 | BOOST_AUTO_TEST_CASE(copy_to_back_inserter) |
| 263 | { |
| 264 | compute::vector<int> device_vector(5, context); |
| 265 | compute::iota(first: device_vector.begin(), last: device_vector.end(), value: 10, queue); |
| 266 | |
| 267 | std::vector<int> host_vector; |
| 268 | compute::copy( |
| 269 | first: device_vector.begin(), |
| 270 | last: device_vector.end(), |
| 271 | result: std::back_inserter(x&: host_vector), |
| 272 | queue |
| 273 | ); |
| 274 | |
| 275 | BOOST_CHECK_EQUAL(host_vector.size(), size_t(5)); |
| 276 | BOOST_CHECK_EQUAL(host_vector[0], 10); |
| 277 | BOOST_CHECK_EQUAL(host_vector[1], 11); |
| 278 | BOOST_CHECK_EQUAL(host_vector[2], 12); |
| 279 | BOOST_CHECK_EQUAL(host_vector[3], 13); |
| 280 | BOOST_CHECK_EQUAL(host_vector[4], 14); |
| 281 | } |
| 282 | |
| 283 | BOOST_AUTO_TEST_CASE(copy_to_stringstream) |
| 284 | { |
| 285 | std::stringstream stream; |
| 286 | |
| 287 | int data[] = { 2, 3, 4, 5, 6, 7, 8, 9 }; |
| 288 | compute::vector<int> vector(data, data + 8, queue); |
| 289 | |
| 290 | compute::copy( |
| 291 | first: vector.begin(), |
| 292 | last: vector.end(), |
| 293 | result: std::ostream_iterator<int>(stream, " " ), |
| 294 | queue |
| 295 | ); |
| 296 | BOOST_CHECK_EQUAL(stream.str(), std::string("2 3 4 5 6 7 8 9 " )); |
| 297 | } |
| 298 | |
| 299 | BOOST_AUTO_TEST_CASE(check_copy_type) |
| 300 | { |
| 301 | // copy from host to device and ensure clEnqueueWriteBuffer() is used |
| 302 | int data[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; |
| 303 | compute::vector<int> a(8, context); |
| 304 | compute::future<void> future = |
| 305 | compute::copy_async(first: data, last: data + 8, result: a.begin(), queue); |
| 306 | BOOST_CHECK( |
| 307 | future.get_event().get_command_type() == CL_COMMAND_WRITE_BUFFER |
| 308 | ); |
| 309 | future.wait(); |
| 310 | CHECK_RANGE_EQUAL(int, 8, a, (1, 2, 3, 4, 5, 6, 7, 8)); |
| 311 | |
| 312 | // copy on the device and ensure clEnqueueCopyBuffer() is used |
| 313 | compute::vector<int> b(8, context); |
| 314 | future = compute::copy_async(first: a.begin(), last: a.end(), result: b.begin(), queue); |
| 315 | BOOST_CHECK( |
| 316 | future.get_event().get_command_type() == CL_COMMAND_COPY_BUFFER |
| 317 | ); |
| 318 | future.wait(); |
| 319 | CHECK_RANGE_EQUAL(int, 8, b, (1, 2, 3, 4, 5, 6, 7, 8)); |
| 320 | |
| 321 | // copy between vectors of different types on the device and ensure |
| 322 | // that the copy kernel is used |
| 323 | compute::vector<short> c(8, context); |
| 324 | future = compute::copy_async(a.begin(), a.end(), c.begin(), queue); |
| 325 | BOOST_CHECK( |
| 326 | future.get_event().get_command_type() == CL_COMMAND_NDRANGE_KERNEL |
| 327 | ); |
| 328 | future.wait(); |
| 329 | CHECK_RANGE_EQUAL(short, 8, c, (1, 2, 3, 4, 5, 6, 7, 8)); |
| 330 | |
| 331 | // copy from device to host and ensure clEnqueueReadBuffer() is used |
| 332 | future = compute::copy_async(first: b.begin(), last: b.end(), result: data, queue); |
| 333 | BOOST_CHECK( |
| 334 | future.get_event().get_command_type() == CL_COMMAND_READ_BUFFER |
| 335 | ); |
| 336 | future.wait(); |
| 337 | CHECK_HOST_RANGE_EQUAL(int, 8, data, (1, 2, 3, 4, 5, 6, 7, 8)); |
| 338 | } |
| 339 | |
| 340 | #ifdef BOOST_COMPUTE_CL_VERSION_2_0 |
| 341 | BOOST_AUTO_TEST_CASE(copy_svm_ptr) |
| 342 | { |
| 343 | REQUIRES_OPENCL_VERSION(2, 0); |
| 344 | |
| 345 | using boost::compute::int_; |
| 346 | |
| 347 | if(bug_in_svmmemcpy(device)){ |
| 348 | std::cerr << "skipping copy_svm_ptr test case" << std::endl; |
| 349 | return; |
| 350 | } |
| 351 | |
| 352 | int_ data[] = { 1, 3, 2, 4 }; |
| 353 | |
| 354 | compute::svm_ptr<int_> ptr = compute::svm_alloc<int_>(context, 4); |
| 355 | compute::copy(data, data + 4, ptr, queue); |
| 356 | |
| 357 | int_ output[] = { 0, 0, 0, 0 }; |
| 358 | compute::copy(ptr, ptr + 4, output, queue); |
| 359 | CHECK_HOST_RANGE_EQUAL(int_, 4, output, (1, 3, 2, 4)); |
| 360 | |
| 361 | compute::svm_free(context, ptr); |
| 362 | } |
| 363 | |
| 364 | BOOST_AUTO_TEST_CASE(copy_async_svm_ptr) |
| 365 | { |
| 366 | REQUIRES_OPENCL_VERSION(2, 0); |
| 367 | |
| 368 | using boost::compute::int_; |
| 369 | |
| 370 | if(bug_in_svmmemcpy(device)){ |
| 371 | std::cerr << "skipping copy_svm_ptr test case" << std::endl; |
| 372 | return; |
| 373 | } |
| 374 | |
| 375 | int_ data[] = { 1, 3, 2, 4 }; |
| 376 | |
| 377 | compute::svm_ptr<int_> ptr = compute::svm_alloc<int_>(context, 4); |
| 378 | boost::compute::future<void> future = |
| 379 | compute::copy_async(data, data + 4, ptr, queue); |
| 380 | future.wait(); |
| 381 | |
| 382 | int_ output[] = { 0, 0, 0, 0 }; |
| 383 | future = |
| 384 | compute::copy_async(ptr, ptr + 4, output, queue); |
| 385 | future.wait(); |
| 386 | CHECK_HOST_RANGE_EQUAL(int_, 4, output, (1, 3, 2, 4)); |
| 387 | |
| 388 | compute::svm_free(context, ptr); |
| 389 | } |
| 390 | #endif // BOOST_COMPUTE_CL_VERSION_2_0 |
| 391 | |
| 392 | BOOST_AUTO_TEST_CASE(copy_to_vector_bool) |
| 393 | { |
| 394 | using compute::uchar_; |
| 395 | |
| 396 | compute::vector<uchar_> vec(2, context); |
| 397 | |
| 398 | // copy to device |
| 399 | bool data[] = {true, false}; |
| 400 | compute::copy(first: data, last: data + 2, result: vec.begin(), queue); |
| 401 | BOOST_CHECK(static_cast<bool>(vec[0]) == true); |
| 402 | BOOST_CHECK(static_cast<bool>(vec[1]) == false); |
| 403 | |
| 404 | // copy to host |
| 405 | std::vector<bool> host_vec(vec.size()); |
| 406 | compute::copy(first: vec.begin(), last: vec.end(), result: host_vec.begin(), queue); |
| 407 | BOOST_CHECK(host_vec[0] == true); |
| 408 | BOOST_CHECK(host_vec[1] == false); |
| 409 | } |
| 410 | |
| 411 | BOOST_AUTO_TEST_SUITE_END() |
| 412 | |