|
| 1 | +# Default-constructed Buffers |
| 2 | + |
| 3 | +Proposal ID | CP021 |
| 4 | +---------------- | --------------------------- |
| 5 | +Name | Default-constructed Buffers |
| 6 | +Date of Creation | 2019-08-20 |
| 7 | +Target | SYCL 1.2.1 |
| 8 | +Status | _Draft_ |
| 9 | +Author | Duncan McBain [Duncan McBain](mailto:duncan@codeplay.com) |
| 10 | +Contributors | Duncan McBain, [Gordon Brown](mailto:gordon@codeplay.com) |
| 11 | + |
| 12 | +## Description |
| 13 | + |
| 14 | +In SYCL, there are no buffer constructors that take no arguments, which means |
| 15 | +that `cl::sycl::buffer` cannot be default-constructed. However, this limits the |
| 16 | +use of buffers in some interfaces. Consider a library function that has an |
| 17 | +optional user-provided allocation for temporary data. The library can avoid |
| 18 | +allocations if the library user so desires by providing a buffer here, but how |
| 19 | +should the user indicate that allocation is allowed? A natural way would be to |
| 20 | +provide a default argument in the API function: |
| 21 | + |
| 22 | +```c++ |
| 23 | +template <typename Allocation_t> |
| 24 | +void foo(Allocation_t input_a, |
| 25 | + Allocation_t input_b, |
| 26 | + Allocation_t temp = Allocation_t{}); |
| 27 | +``` |
| 28 | +
|
| 29 | +For `Allocation_t` types that are pointer-like, the library can check for |
| 30 | +`nullptr` and allocate if this condition is true. A similar mechanism that |
| 31 | +allows `cl::sycl::buffer` to be constructed and checked for usability would |
| 32 | +aid their use in this style of generic interface. |
| 33 | +
|
| 34 | +## Proposal |
| 35 | +
|
| 36 | +The `cl::sycl::buffer` class should be augmented with an additional constructor |
| 37 | +that takes no arguments, which initialises the buffer with a zero-size range. |
| 38 | +```c++ |
| 39 | +namespace cl { |
| 40 | +namespace sycl { |
| 41 | +template <typename T, int dimensions = 1, |
| 42 | +typename AllocatorT = cl::sycl::buffer_allocator> |
| 43 | +class buffer { |
| 44 | + buffer(); |
| 45 | +
|
| 46 | + bool is_valid() const noexcept; |
| 47 | +
|
| 48 | + explicit operator bool() const noexcept; |
| 49 | +}; |
| 50 | +} // namespace sycl |
| 51 | +} // namespace cl |
| 52 | +``` |
| 53 | +The template arguments should remain the same, so that the argument can be |
| 54 | +rebound to a new `buffer` instance later using the copy constructor. |
| 55 | + |
| 56 | +The `is_valid()` call would allow the programmer to query whether or not |
| 57 | +the buffer can be used, i.e. whether or not it was constructed with a range of |
| 58 | +size zero. The explicit conversion operator would call this same function but |
| 59 | +allow its use in `if` statements. |
| 60 | + |
| 61 | +Requesting access from a default-constructed buffer should throw an exception. |
| 62 | +It is not meaningful to use a zero-sized allocation on-device. Since there is |
| 63 | +no allocation associated with the `buffer`, `cl::sycl::buffer::set_final_data` |
| 64 | +and `cl::sycl::buffer::set_write_back` should behave as if the `buffer` had a |
| 65 | +final pointer of `nullptr` at all times. The other functions in the `buffer` |
| 66 | +API should behave as though the buffer were constructed with a `range` of size |
| 67 | +zero and otherwise behave normally. |
| 68 | + |
| 69 | +## Sample code |
| 70 | + |
| 71 | +```c++ |
| 72 | +#include <CL/sycl.hpp> |
| 73 | +using namespace sycl = cl::sycl; |
| 74 | +using namespace access = sycl::access; |
| 75 | + |
| 76 | +class name; |
| 77 | + |
| 78 | +void api_func(sycl::queue& q, |
| 79 | + sycl::buffer<float, 1> input, sycl::buffer<float, 1> output, |
| 80 | + sycl::buffer<float, 1> workspace = sycl::buffer<float, 1>{}) { |
| 81 | + if (!workspace) { |
| 82 | + // calculate required size for workspace |
| 83 | + constexper auto size = 2048llu; |
| 84 | + workspace = sycl::buffer<float, 1>{sycl::range<1>{size}}; |
| 85 | + } |
| 86 | + // use buffer |
| 87 | + q.submit([&] (sycl::handler& cgh) { |
| 88 | + auto acc_in = input.get_access<access::mode::read>(cgh); |
| 89 | + auto acc_out = input.get_access<access::mode::write>(cgh); |
| 90 | + auto acc_workspace = input.get_access<access::mode::read_write>(cgh); |
| 91 | + cgh.single_task<name>([=]() { |
| 92 | + // uses accessors |
| 93 | + }); |
| 94 | + }); |
| 95 | +} |
| 96 | +``` |
0 commit comments