Buffers#
sycl::buffer
#
template <typename T, int Dimensions = 1,
typename AllocatorT = sycl::buffer_allocator<std::remove_const_t<T>>>
class buffer;
Template parameters
|
Type of data in buffer. |
|
Dimensionality of data: 1, 2, or 3. |
|
Allocator for buffer data. |
The sycl::buffer
class defines a shared array of one,
two or three dimensions that can be used by the SYCL kernel
and has to be accessed using accessor classes.
Buffers are templated on both the type of their data, and the number
of dimensions that the data is stored and accessed through.
A sycl::buffer
does not map to only one underlying backend object,
and all SYCL backend memory objects may be temporary for use within a
command group on a specific device.
The underlying data type of a buffer T
must be device copyable.
Some overloads of the sycl::buffer
constructor initialize the buffer
contents by copying objects from host memory while other overloads
construct the buffer without copying objects from the host. For the
overloads that do not copy host objects, the initial state of the
objects in the buffer depends on whether T
is an implicit-lifetime
type (as defined in the C++ core language).
If
T
is an implicit-lifetime type, objects of that type are implicitly created in the buffer with indeterminate values.For other types, these constructor overloads merely allocate uninitialized memory, and the application is responsible for constructing objects by calling placement-new and for destroying them later by manually calling the object's destructor.
The SYCL buffer class template provides the Common Reference Semantics.
The sycl::buffer
class template takes a template parameter AllocatorT
for specifying an allocator which is used by the SYCL runtime when allocating
temporary memory on the host. If no template argument is provided,
then the default allocator for the sycl::buffer
class buffer_allocator<T>
will be used.
See also
SYCL Specification Section 4.7.2
Member Types#
Type |
Description |
---|---|
|
Type of buffer elements ( |
|
Reference type of buffer elements ( |
|
Constant reference type of buffer element ( |
|
Type of allocator for buffer data ( |
(constructors)#
Each constructor takes as the last parameter an optional
sycl::property_list
to provide properties to the sycl::buffer
.
Zero or more properties can be provided to the
constructed sycl::buffer
via an instance of sycl::property_list
.
For the overloads that do copy objects from host memory, the hostData
pointer must point to at least N bytes of memory where N is
sizeof(T) * bufferRange.size()
.
If N is zero, hostData
is permitted to be a null pointer.
Constructors 1-2
buffer(const sycl::range<Dimensions>& bufferRange,
const sycl::property_list& propList = {});
buffer(const sycl::range<Dimensions>& bufferRange,
AllocatorT allocator,
const sycl::property_list& propList = {});
Construct a sycl::buffer
instance with uninitialized memory.
The constructed sycl::buffer
will use the allocator
parameter provided when allocating memory on the host.
If this parameter is not specified, the constructed
sycl::buffer
will use a default constructed
AllocatorT
when allocating memory on the host.
Data is not written back to the host on destruction of the buffer
unless the buffer has a valid non-null pointer specified via
the member function set_final_data()
.
Constructors 3-4
buffer(T* hostData,
const sycl::range<Dimensions>& bufferRange,
const sycl::property_list& propList = {});
buffer(T* hostData,
const sycl::range<Dimensions>& bufferRange,
AllocatorT allocator,
const sycl::property_list& propList = {});
Construct a sycl::buffer
instance with the
hostData
parameter provided.
The buffer is initialized with the memory specified by hostData
,
and the buffer assumes exclusive access to this memory for the
duration of its lifetime.
The constructed sycl::buffer
will use the allocator
parameter provided when allocating memory on the host.
If this parameter is not specified, the constructed
sycl::buffer
will use a default constructed
AllocatorT
when allocating memory on the host.
Constructors 5-6
buffer(const T* hostData,
const sycl::range<Dimensions>& bufferRange,
const sycl::property_list& propList = {});
buffer(const T* hostData,
const sycl::range<Dimensions>& bufferRange,
AllocatorT allocator,
const sycl::property_list& propList = {});
Construct a sycl::buffer
instance with the hostData
parameter
provided. The buffer assumes exclusive access to this memory for
the duration of its lifetime.
The constructed sycl::buffer
will use the allocator
parameter provided when allocating memory on the host.
If this parameter is not specified, the constructed
sycl::buffer
will use a default constructed
AllocatorT
when allocating memory on the host.
The host address is const T
, so the host accesses can be
read-only. However, the typename T
is not const
so the
device accesses can be both read and write accesses.
Since the hostData
is const
, this buffer is only initialized
with this memory and there is no write back after its destruction,
unless the sycl::buffer
has another valid non-null final data
address specified via the member function set_final_data()
after construction of the sycl::buffer
.
Constructors 7-8
template <typename Container>
buffer(Container& container,
const sycl::property_list& propList = {});
template <typename Container>
buffer(Container& container,
AllocatorT allocator,
const sycl::property_list& propList = {});
Construct a one dimensional sycl::buffer
instance from the
elements starting at std::data(container)
and containing
std::size(container)
number of elements.
The buffer is initialized with the contents of container
,
and the buffer assumes exclusive access to container
for
the duration of its lifetime.
Data is written back to container
before the completion of
sycl::buffer
destruction if the return type of
std::data(container)
is not const
.
The constructed sycl::buffer
will use the allocator
parameter provided when allocating memory on the host.
If this parameter is not specified, the constructed
sycl::buffer
will use a default constructed
AllocatorT
when allocating memory on the host.
This constructor is only defined for a buffer parameterized
with Dimensions == 1
, and when std::data(container)
is convertible to T*
.
Constructors 9-12
buffer(const std::shared_ptr<T>& hostData,
const sycl::range<Dimensions>& bufferRange,
const sycl::property_list& propList = {});
buffer(const std::shared_ptr<T[]>& hostData,
const sycl::range<Dimensions>& bufferRange,
const sycl::property_list& propList = {});
buffer(const std::shared_ptr<T>& hostData,
const sycl::range<Dimensions>& bufferRange,
AllocatorT allocator,
const sycl::property_list& propList = {});
buffer(const std::shared_ptr<T[]>& hostData,
const sycl::range<Dimensions>& bufferRange,
AllocatorT allocator,
const sycl::property_list& propList = {});
When hostData
is not empty, construct a sycl::buffer
with the contents of its stored pointer. The buffer assumes
exclusive access to this memory for the duration of its lifetime.
The buffer also creates its own internal copy of the std::shared_ptr
that shares ownership of the hostData
memory, which means the
application can safely release ownership of this std::shared_ptr
when the constructor returns.
When hostData
is empty, construct a SYCL
buffer with uninitialized memory.
The constructed sycl::buffer
will use the allocator
parameter provided when allocating memory on the host.
If this parameter is not specified, the constructed
sycl::buffer
will use a default constructed
AllocatorT
when allocating memory on the host.
Constructors 13-14
template <typename InputIterator>
buffer(InputIterator first, InputIterator last,
const sycl::property_list& propList = {});
template <typename InputIterator>
buffer(InputIterator first, InputIterator last,
AllocatorT allocator = {},
const sycl::property_list& propList = {});
Create a new allocated one dimension sycl::buffer
initialized
from the given elements ranging from first
up to one before last
.
The data is copied to an intermediate memory position by the runtime.
Data is not written back to the same iterator set provided.
However, if the sycl::buffer
has a valid non-constant iterator
specified via the member function set_final_data()
,
data will be copied back to that iterator.
The constructed sycl::buffer
will use the allocator
parameter provided when allocating memory on the host.
If this parameter is not specified, the constructed
sycl::buffer
will use a default constructed
AllocatorT
when allocating memory on the host.
Constructor 15
buffer(sycl::buffer& b,
const sycl::id<Dimensions>& baseIndex,
const sycl::range<Dimensions>& subRange)
Create a new sub-buffer without allocation to have separate accessors later.
b
is the buffer with the real data, which must not
be a sub-buffer.
baseIndex
specifies the origin of the sub-buffer
inside the buffer b
.
subRange
specifies the size of the sub-buffer.
The offset and range specified by baseIndex
and subRange
together must represent a contiguous region of the
original sycl::buffer
.
The origin (based on baseIndex
) of the sub-buffer being
constructed must be a multiple of the memory base address
alignment of each sycl::device which accesses data from
the buffer. This value is retrievable via the sycl::device
class info query sycl::info::device::mem_base_addr_align
.
Violating this requirement causes the implementation to throw
an exception
with the errc::invalid
error code from
the sycl::accessor constructor (if the accessor
is not a placeholder) or from sycl::handler::require()
(if the accessor is a placeholder).
If the accessor is bound to a command group with a secondary
queue, the sub-buffer's alignment must be compatible with
both the primary queue's device and the secondary queue's
device, otherwise this exception is thrown.
Template parameters
|
Type of the |
|
Type of iterator used to initialize the buffer. |
Parameters
|
sycl::range specifies the dimensions of the buffer. |
|
Allocator for the buffer data. In case this parameter
is absent, the |
|
See Buffer properties. |
|
Pointer to host memory to hold data. |
|
Beginning iterator to initialize the buffer. |
|
Ending iterator to initialize the buffer. |
|
Parent buffer used to initialize this buffer. |
|
Origin of the sub-buffer. |
|
Dimensions of the sub-buffer. |
Exceptions
errc::invalid
An exception with this error code will be thrown in the constructor 15 in such cases:
If the sum of
baseIndex
andsubRange
in any dimension exceeds the parent buffer (b
) size (bufferRange
) in that dimension.If a non-contiguous region of a buffer is requested when constructing a sub-buffer.
If
b
is a sub-buffer.
Example
See Example 1.
Member functions#
get_range
#
sycl::range<Dimensions> get_range() const;
Return a sycl::range object representing the size of the buffer in terms of number of elements in each dimension as passed to the constructor.
size
#
size_t size() const noexcept;
Returns the total number of elements in the buffer.
Equal to get_range()[0] * ... * get_range()[Dimensions-1]
.
get_count
#
size_t get_count() const;
Deprecated. Returns the same value as size().
byte_size
#
size_t byte_size() const noexcept;
Returns the size of the buffer storage in bytes.
Equal to size()*sizeof(T)
.
get_size
#
size_t get_size() const;
Deprecated. Returns the same value as byte_size()
.
get_allocator
#
AllocatorT get_allocator() const
Returns the allocator provided to the buffer.
get_access
#
template <sycl::access_mode Mode = sycl::access_mode::read_write,
sycl::target Targ = sycl::target::device>
sycl::accessor<T, Dimensions, Mode, Targ> get_access(sycl::handler& commandGroupHandler);
Returns a valid sycl::accessor to the buffer with the specified access mode and target in the command group buffer.
The value of target can be sycl::target::device
or
sycl::target::constant_buffer
.
template <sycl::access_mode Mode = sycl::access_mode::read_write,
sycl::target Targ = sycl::target::device>
sycl::accessor<T, Dimensions, Mode, Targ> get_access(sycl::handler& commandGroupHandler,
sycl::range<Dimensions> accessRange,
sycl::id<Dimensions> accessOffset = {});
Returns a valid sycl::accessor to the buffer with the specified access mode and target in the command group buffer. The accessor is a ranged accessor, where the range starts at the given offset from the beginning of the buffer.
The value of target can be sycl::target::device
or
sycl::target::constant_buffer
.
template <typename... Ts>
auto get_access(Ts... args);
Returns a valid sycl::accessor as if constructed via passing the
buffer and all provided arguments to the sycl::accessor
constructor.
Possible implementation: return sycl::accessor{*this, args...};
Deprecated in SYCL 2020
template <sycl::access_mode Mode>
sycl::accessor<T, Dimensions, Mode, sycl::target::host_buffer> get_access();
Deprecated in SYCL 2020. Use get_host_access()
instead.
Returns a valid host sycl::accessor
to the buffer with the
specified access mode and target.
template <sycl::access_mode Mode>
sycl::accessor<T, Dimensions, Mode, sycl::target::host_buffer>
get_access(sycl::range<Dimensions> accessRange,
sycl::id<Dimensions> accessOffset = {});
Deprecated in SYCL 2020. Use get_host_access()
instead.
Returns a valid host sycl::accessor
to the buffer with the specified
access mode and target.
The accessor is a ranged accessor, where the range starts at the given
offset from the beginning of the buffer.
The value of target can only be sycl::target::host_buffer
.
Template parameters
|
See sycl::access_mode. |
|
See Access targets. |
Parameters
|
Command group that uses the accessor. |
|
Dimensions of the sub-buffer that is accessed. |
|
Origin of the sub-buffer that is accessed. |
Exceptions
errc::invalid
If the sum of
accessRange
andaccessOffset
exceeds the range of the buffer in any dimension.
get_host_access
#
template <typename... Ts>
auto get_host_access(Ts... args);
Returns a valid sycl::host_accessor as if constructed via passing the
buffer and all provided arguments to the sycl::host_accessor
constructor.
Possible implementation: return sycl::host_accessor{*this, args...};
set_final_data
#
template <typename Destination = std::nullptr_t>
void set_final_data(Destination finalData = nullptr);
The finalData
points to where the outcome of all the buffer
processing is going to be copied to at destruction time, if the buffer
was involved with a write accessor
Note that a raw pointer is a special case of output iterator and thus defines the host memory to which the result is to be copied.
In the case of a weak pointer, the output is not updated if the weak pointer has expired.
If Destination
is std::nullptr_t
, then the copy back will not happen.
Template parameters
|
Output iterator or |
Parameters
|
Indicates where data is copied at destruction time. |
set_write_back
#
void set_write_back(bool flag = true);
This member function allows dynamically forcing or canceling the
write-back of the data of a buffer on destruction according to
the value of flag
.
Forcing the write-back is similar to what happens during a normal write-back.
If there is nowhere to write-back, using this function does not have any effect.
is_sub_buffer
#
bool is_sub_buffer() const;
Returns true
if this sycl::buffer
is a sub-buffer,
otherwise returns false
.
reinterpret
#
A sycl::buffer
can construct an instance of a sycl::buffer
that reinterprets the original sycl::buffer
with a different
type, dimensionality and range using the member function reinterpret
.
template <typename ReinterpretT, int ReinterpretDim>
sycl::buffer<ReinterpretT, ReinterpretDim,
typename std::allocator_traits<AllocatorT>::template rebind_alloc<
std::remove_const_t<ReinterpretT>>>
reinterpret(sycl::range<ReinterpretDim> reinterpretRange) const;
Creates and returns a reinterpreted sycl::buffer
with the
type specified by ReinterpretT
, dimensions specified by
ReinterpretDim
and range specified by reinterpretRange
.
The buffer object being reinterpreted can be a SYCL sub-buffer
that was created from a sycl::buffer
.
Reinterpreting a sub-buffer provides a reinterpreted view of the
sub-buffer only, and does not change the offset or size of the
sub-buffer view (in bytes) relative to the parent sycl::buffer
.
template <typename ReinterpretT, int ReinterpretDim = Dimensions>
sycl::buffer<ReinterpretT, ReinterpretDim,
typename std::allocator_traits<AllocatorT>::template rebind_alloc<
std::remove_const_t<ReinterpretT>>>
reinterpret() const;
Creates and returns a reinterpreted sycl::buffer
with the type specified by
ReinterpretT
and dimensions specified by ReinterpretDim
.
Only valid when (ReinterpretDim == 1)
or when
((ReinterpretDim == Dimensions) && (sizeof(ReinterpretT) == sizeof(T)))
.
The buffer object being reinterpreted can be a SYCL sub-buffer that was created from a SYCL buffer.
Reinterpreting a sub-buffer provides a reinterpreted view of the
sub-buffer only, and does not change the offset or size of the
sub-buffer view (in bytes) relative to the parent sycl::buffer
.
Template parameters
|
Type of the new buffer element. |
|
Dimensions of the new buffer. |
Parameters
|
Dimensionality of the new buffer. |
Exceptions
errc::invalid
If the total size in bytes represented by the type and range of the reinterpreted
sycl::buffer
(or sub-buffer) does not equal the total size in bytes represented by the type and range of thissycl::buffer
(or sub-buffer).If the total size in bytes represented by this
sycl::buffer
(or sub-buffer) is not evenly divisible bysizeof(ReinterpretT)
.
Buffer properties#
The properties that can be provided when
constructing the sycl::buffer
.
namespace sycl::property {
namespace buffer {
class use_host_ptr;
class use_mutex;
class context_bound;
} // namespace buffer
} // namespace sycl::property
sycl::property::buffer::use_host_ptr
#
namespace sycl::property::buffer {
class use_host_ptr {
public:
use_host_ptr() = default;
};
} // namespace sycl::property::buffer
The sycl::property::buffer::use_host_ptr
property adds the requirement
that the SYCL runtime must not allocate any memory for the sycl::buffer
and instead uses the provided host pointer directly. This prevents the SYCL
runtime from allocating additional temporary storage on the host.
This property has a special guarantee for buffers that are constructed
from a hostData
pointer. If a sycl::host_accessor
is constructed from
such a buffer, then the address of the reference type returned from the
accessor's member functions such as operator[](id<>)
will be the same
as the corresponding hostData
address.
(constructors)#
sycl::property::buffer::use_host_ptr::use_host_ptr();
Constructs a sycl::property::buffer::use_host_ptr
property instance.
sycl::property::buffer::use_mutex
#
namespace sycl::property::buffer {
class use_mutex {
public:
use_mutex(std::mutex& mutexRef);
std::mutex* get_mutex_ptr() const;
};
} // namespace sycl::property::buffer
The sycl::property::buffer::use_mutex
property is valid for the
sycl::buffer
, unsampled_image and sampled_image classes.
The property adds the requirement that the memory which is owned by
the sycl::buffer
can be shared with the application via a
std::mutex
provided to the property.
The mutex is locked by the runtime whenever the data is in use and
unlocked otherwise. Data is synchronized with hostData
, when
the mutex is unlocked by the runtime.
(constructors)#
sycl::property::buffer::use_mutex::use_mutex(std::mutex& mutexRef);
Constructs a sycl::property::buffer::use_mutex
property instance
with a reference to mutexRef
parameter provided.
get_mutex_ptr
#
std::mutex* sycl::property::buffer::use_mutex::get_mutex_ptr() const;
Returns the std::mutex
which was specified when constructing
this sycl::property::buffer::use_mutex
property.
sycl::property::buffer::context_bound
#
namespace sycl::property::buffer {
class context_bound {
public:
context_bound(context boundContext);
context get_context() const;
};
} // namespace sycl::property::buffer
The sycl::property::buffer::context_bound
property adds the
requirement that the sycl::buffer
can only be associated
with a single sycl::context that is provided to the property.
(constructors)#
sycl::property::buffer::context_bound(sycl::context boundContext);
Constructs a sycl::property::buffer::context_bound
property
instance with a copy of a sycl::context.
get_context
#
sycl::context sycl::property::buffer::context_bound::get_context() const;
Returns the sycl::context which was specified when constructing
this sycl::property::buffer::context_bound
property.
Buffer synchronization rules#
Buffer lifetime and destruction#
Buffers are reference-counted. When a buffer value is constructed from another buffer, the two values reference the same buffer and a reference count is incremented. When a buffer value is destroyed, the reference count is decremented. Only when there are no more buffer values that reference a specific buffer is the actual buffer destroyed and the buffer destruction behavior defined below is followed.
If any error occurs on buffer destruction, it is reported via the associated queue's asynchronous error handling mechanism.
The basic rule for the blocking behavior of a buffer destructor is that it blocks if there is some data to write back because a write accessor on it has been created, or if the buffer was constructed with attached host memory and is still in use.
More precisely:
A buffer can be constructed from a sycl::range (and without a
hostData
pointer). The memory management for this type of buffer is entirely handled by the SYCL system. The destructor for this type of buffer does not need to block, even if work on the buffer has not completed. Instead, the SYCL system frees any storage required for the buffer asynchronously when it is no longer in use in queues. The initial contents of the buffer are unspecified.A buffer can be constructed from a
hostData
pointer. The buffer will use this host memory for its full lifetime, but the contents of this host memory are unspecified for the lifetime of the buffer. If the host memory is modified on the host or if it is used to construct another buffer or image during the lifetime of this buffer, then the results are undefined. The initial contents of the buffer will be the contents of the host memory at the time of construction.When the buffer is destroyed, the destructor will block until all work in queues on the buffer have completed, then copy the contents of the buffer back to the host memory (if required) and then return.
If the type of the host data is
const
, then the buffer is read-only; only read accessors are allowed on the buffer and no-copy-back to host memory is performed (although the host memory must still be kept available for use by SYCL). When using the default buffer allocator, the constantness of the type will be removed in order to allow host allocation of memory, which will allow temporary host copies of the data by the SYCL runtime, for example for speeding up host accesses.If the type of the host data is not
const
but the pointer to host data isconst
, then the read-only restriction applies only on host and not on device accesses.When the buffer is destroyed, the destructor will block until all work in queues on the buffer have completed.
A buffer can be constructed using a
std::shared_ptr
to host data. This pointer is shared between the SYCL application and the runtime. In order to allow synchronization between the application and the runtime astd::mutex
is used which will be locked by the runtime whenever the data is in use, and unlocked when it is no longer needed.The
std::shared_ptr
reference counting is used in order to prevent destroying the buffer host data prematurely. If thestd::shared_ptr
is deleted from the user application before buffer destruction, the buffer can continue securely because the pointer hasn't been destroyed yet. It will not copy data back to the host before destruction, however, as the application side has already deleted its copy.Note
Since there is an implicit conversion of a
std::unique_ptr
to astd::shared_ptr
, astd::unique_ptr
can also be used to pass the ownership to the SYCL runtime.A buffer can be constructed from a pair of iterator values. In this case, the buffer construction will copy the data from the data range defined by the iterator pair. The destructor will not copy back any data and does not need to block.
A buffer can be constructed from a container on which
std::data(container)
andstd::size(container)
are well-formed. The initial contents of the buffer will be the contents of the container at the time of construction.The buffer may use the memory within the container for its full lifetime, and the contents of this memory are unspecified for the lifetime of the buffer. If the container memory is modified by the host during the lifetime of this buffer, then the results are undefined.
When the buffer is destroyed, the destructor will block until all work in queues on the buffer have completed. If the return type of
std::data(container)
is notconst
then the destructor will also copy the contents of the buffer to the container (if required).
If set_final_data()
is used to change where to write the data
back to, then the destructor of the buffer will block if a write
accessor on it has been created.
Sub-buffers#
A sub-buffer object can be created, which is a sub-range reference to a base buffer. This sub-buffer can be used to create accessors to the base buffer, which have access to the range specified at time of construction of the sub-buffer. Sub-buffers cannot be created from sub-buffers, but only from a base buffer which is not already a sub-buffer.
Sub-buffers must be constructed from a contiguous region of memory in a buffer. This requirement is potentially non-intuitive when working with buffers that have dimensionality larger than one, but maps to one-dimensional SYCL backend native allocations without performance cost due to index mapping computation.
Example
See Example 1.
Example 1#
An example of creating sub-buffers from a parent buffer:
1#include <sycl/sycl.hpp>
2
3#include <iostream>
4
5int main() {
6
7 // Create 2-d buffer with 8x8 ints
8 sycl::buffer<int, 2> parent_buffer{sycl::range<2>{8, 8}};
9
10 try {
11 // OK: Contiguous region from middle of buffer
12 sycl::buffer<int, 2> sub_buf1{parent_buffer,
13 /*offset*/ sycl::range<2>{2, 0},
14 /*size*/ sycl::range<2>{2, 8}};
15
16 std::cout << "sub_buf1 created successfully" << std::endl;
17 } catch (const sycl::exception &e) {
18 std::cerr << "Exception while creating sub_buf1: " << e.what() << std::endl;
19 }
20
21 try {
22 // invalid exception: Non-contiguous regions of 2-d buffer
23 sycl::buffer<int, 2> sub_buf2{parent_buffer,
24 /*offset*/ sycl::range<2>{2, 0},
25 /*size*/ sycl::range<2>{2, 2}};
26
27 std::cout << "sub_buf2 created successfully" << std::endl;
28 } catch (const sycl::exception &e) {
29 std::cerr << "Exception while creating sub_buf2: " << e.what() << std::endl;
30 }
31
32 try {
33 // invalid exception: Non-contiguous regions of 2-d buffer
34 sycl::buffer<int, 2> sub_buf3{parent_buffer,
35 /*offset*/ sycl::range<2>{2, 2},
36 /*size*/ sycl::range<2>{2, 6}};
37
38 std::cout << "sub_buf3 created successfully" << std::endl;
39 } catch (const sycl::exception &e) {
40 std::cerr << "Exception while creating sub_buf3: " << e.what() << std::endl;
41 }
42
43 try {
44 // invalid exception: Out-of-bounds size
45 sycl::buffer<int, 2> sub_buf4{parent_buffer,
46 /*offset*/ sycl::range<2>{2, 2},
47 /*size*/ sycl::range<2>{2, 8}};
48
49 std::cout << "sub_buf4 created successfully" << std::endl;
50 } catch (const sycl::exception &e) {
51 std::cerr << "Exception while creating sub_buf4: " << e.what() << std::endl;
52 }
53
54 return 0;
55}
Output:
sub_buf1 created successfully
Exception while creating sub_buf2: Requested sub-buffer region is not contiguous -30 (PI_ERROR_INVALID_VALUE)
Exception while creating sub_buf3: Requested sub-buffer region is not contiguous -30 (PI_ERROR_INVALID_VALUE)
Exception while creating sub_buf4: Requested sub-buffer size exceeds the size of the parent buffer -30 (PI_ERROR_INVALID_VALUE)