DPC++ Runtime
Runtime libraries for oneAPI DPC++
sycl_mem_obj_t.hpp
Go to the documentation of this file.
1 //==------------ sycl_mem_obj_t.hpp - SYCL standard header file ------------==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #pragma once
10 
12 #include <sycl/detail/common.hpp>
13 #include <sycl/detail/export.hpp>
16 #include <sycl/event.hpp>
19 #include <sycl/property_list.hpp>
20 #include <sycl/range.hpp>
21 
22 #include <atomic>
23 #include <cstring>
24 #include <memory>
25 #include <type_traits>
26 
27 namespace sycl {
28 inline namespace _V1 {
29 namespace detail {
30 
31 // Forward declarations
32 class context_impl;
33 class event_impl;
34 class plugin;
35 
36 using ContextImplPtr = std::shared_ptr<context_impl>;
37 using EventImplPtr = std::shared_ptr<event_impl>;
38 
39 // The class serves as a base for all SYCL memory objects.
40 class __SYCL_EXPORT SYCLMemObjT : public SYCLMemObjI {
41 
42  // The check for output iterator is commented out as it blocks set_final_data
43  // with void * argument to be used.
44  // TODO: Align these checks with the SYCL specification when the behaviour
45  // with void * is clarified.
46  template <typename T>
47  using EnableIfOutputPointerT = std::enable_if_t<
48  /*is_output_iterator<T>::value &&*/ std::is_pointer<T>::value>;
49 
50  template <typename T>
51  using EnableIfOutputIteratorT = std::enable_if_t<
52  /*is_output_iterator<T>::value &&*/ !std::is_pointer<T>::value>;
53 
54 public:
55  SYCLMemObjT(const size_t SizeInBytes, const property_list &Props,
56  std::unique_ptr<SYCLMemObjAllocator> Allocator)
57  : MAllocator(std::move(Allocator)), MProps(Props), MInteropEvent(nullptr),
58  MInteropContext(nullptr), MInteropMemObject(nullptr),
59  MOpenCLInterop(false), MHostPtrReadOnly(false), MNeedWriteBack(true),
60  MSizeInBytes(SizeInBytes), MUserPtr(nullptr), MShadowCopy(nullptr),
61  MUploadDataFunctor(nullptr), MSharedPtrStorage(nullptr),
62  MHostPtrProvided(false) {}
63 
64  SYCLMemObjT(const property_list &Props,
65  std::unique_ptr<SYCLMemObjAllocator> Allocator)
66  : SYCLMemObjT(/*SizeInBytes*/ 0, Props, std::move(Allocator)) {}
67 
68  SYCLMemObjT(pi_native_handle MemObject, const context &SyclContext,
69  const size_t SizeInBytes, event AvailableEvent,
70  std::unique_ptr<SYCLMemObjAllocator> Allocator);
71 
72  SYCLMemObjT(cl_mem MemObject, const context &SyclContext,
73  event AvailableEvent,
74  std::unique_ptr<SYCLMemObjAllocator> Allocator)
75  : SYCLMemObjT(pi::cast<pi_native_handle>(MemObject), SyclContext,
76  /*SizeInBytes*/ (size_t)0, AvailableEvent,
77  std::move(Allocator)) {}
78 
79  SYCLMemObjT(pi_native_handle MemObject, const context &SyclContext,
80  bool OwnNativeHandle, event AvailableEvent,
81  std::unique_ptr<SYCLMemObjAllocator> Allocator);
82 
83  SYCLMemObjT(pi_native_handle MemObject, const context &SyclContext,
84  bool OwnNativeHandle, event AvailableEvent,
85  std::unique_ptr<SYCLMemObjAllocator> Allocator,
88  range<3> Range3WithOnes, unsigned Dimensions, size_t ElementSize);
89 
90  virtual ~SYCLMemObjT() = default;
91 
92  const PluginPtr &getPlugin() const;
93 
94  size_t getSizeInBytes() const noexcept override { return MSizeInBytes; }
95  __SYCL2020_DEPRECATED("get_count() is deprecated, please use size() instead")
96  size_t get_count() const { return size(); }
97  size_t size() const noexcept {
98  size_t AllocatorValueSize = MAllocator->getValueSize();
99  return (getSizeInBytes() + AllocatorValueSize - 1) / AllocatorValueSize;
100  }
101 
102  template <typename propertyT> bool has_property() const noexcept {
103  return MProps.has_property<propertyT>();
104  }
105 
106  template <typename propertyT> propertyT get_property() const {
107  return MProps.get_property<propertyT>();
108  }
109 
110  void addOrReplaceAccessorProperties(const property_list &PropertyList) {
111  MProps.add_or_replace_accessor_properties(PropertyList);
112  }
113 
115  MProps.delete_accessor_property(Kind);
116  }
117 
118  const std::unique_ptr<SYCLMemObjAllocator> &get_allocator_internal() const {
119  return MAllocator;
120  }
121 
122  void *allocateHostMem() override { return MAllocator->allocate(size()); }
123 
124  void releaseHostMem(void *Ptr) override {
125  if (Ptr)
126  MAllocator->deallocate(Ptr, size());
127  }
128 
129  void releaseMem(ContextImplPtr Context, void *MemAllocation) override;
130 
131  void *getUserPtr() const {
132  return MOpenCLInterop ? static_cast<void *>(MInteropMemObject) : MUserPtr;
133  }
134 
135  void set_write_back(bool NeedWriteBack) { MNeedWriteBack = NeedWriteBack; }
136 
137  void set_final_data(std::nullptr_t) { MUploadDataFunctor = nullptr; }
138 
140  MUploadDataFunctor = [this]() {
141  if (MSharedPtrStorage.use_count() > 1) {
142  void *FinalData = const_cast<void *>(MSharedPtrStorage.get());
143  updateHostMemory(FinalData);
144  }
145  };
146  MHostPtrProvided = true;
147  }
148 
150  const std::function<void(const std::function<void(void *const Ptr)> &)>
151  &FinalDataFunc) {
152 
153  auto UpdateFunc = [this](void *const Ptr) { updateHostMemory(Ptr); };
154  MUploadDataFunctor = [FinalDataFunc, UpdateFunc]() {
155  FinalDataFunc(UpdateFunc);
156  };
157  MHostPtrProvided = true;
158  }
159 
160 protected:
161  void updateHostMemory(void *const Ptr);
162 
163  // Update host with the latest data + notify scheduler that the memory object
164  // is going to die. After this method is finished no further operations with
165  // the memory object is allowed. This method is executed from child's
166  // destructor. This cannot be done in SYCLMemObjT's destructor as child's
167  // members must be alive.
168  void updateHostMemory();
169 
170 public:
171  bool useHostPtr() {
172  return has_property<property::buffer::use_host_ptr>() ||
173  has_property<property::image::use_host_ptr>();
174  }
175 
176  bool canReadHostPtr(void *HostPtr, const size_t RequiredAlign) {
177  bool Aligned =
178  (reinterpret_cast<std::uintptr_t>(HostPtr) % RequiredAlign) == 0;
179  return Aligned || useHostPtr();
180  }
181 
182  bool canReuseHostPtr(void *HostPtr, const size_t RequiredAlign) {
183  return !MHostPtrReadOnly && canReadHostPtr(HostPtr, RequiredAlign);
184  }
185 
186  void handleHostData(void *HostPtr, const size_t RequiredAlign) {
187  MHostPtrProvided = true;
188  if (!MHostPtrReadOnly && HostPtr) {
189  set_final_data([HostPtr](const std::function<void(void *const Ptr)> &F) {
190  F(HostPtr);
191  });
192  }
193 
194  if (HostPtr) {
195  if (canReuseHostPtr(HostPtr, RequiredAlign)) {
196  MUserPtr = HostPtr;
197  } else if (canReadHostPtr(HostPtr, RequiredAlign)) {
198  MUserPtr = HostPtr;
199  MCreateShadowCopy = [this, RequiredAlign, HostPtr]() -> void {
200  setAlign(RequiredAlign);
201  MShadowCopy = allocateHostMem();
202  MUserPtr = MShadowCopy;
203  std::memcpy(MUserPtr, HostPtr, MSizeInBytes);
204  };
205  } else {
206  setAlign(RequiredAlign);
207  MShadowCopy = allocateHostMem();
208  MUserPtr = MShadowCopy;
209  std::memcpy(MUserPtr, HostPtr, MSizeInBytes);
210  }
211  }
212  }
213 
214  void handleHostData(const void *HostPtr, const size_t RequiredAlign) {
215  MHostPtrReadOnly = true;
216  handleHostData(const_cast<void *>(HostPtr), RequiredAlign);
217  }
218 
219  void handleHostData(const std::shared_ptr<void> &HostPtr,
220  const size_t RequiredAlign, bool IsConstPtr) {
221  MHostPtrProvided = true;
222  MSharedPtrStorage = HostPtr;
223  MHostPtrReadOnly = IsConstPtr;
224  if (HostPtr) {
225  if (!MHostPtrReadOnly)
226  set_final_data_from_storage();
227 
228  if (canReuseHostPtr(HostPtr.get(), RequiredAlign)) {
229  MUserPtr = HostPtr.get();
230  } else if (canReadHostPtr(HostPtr.get(), RequiredAlign)) {
231  MUserPtr = HostPtr.get();
232  MCreateShadowCopy = [this, RequiredAlign, HostPtr]() -> void {
233  setAlign(RequiredAlign);
234  MShadowCopy = allocateHostMem();
235  MUserPtr = MShadowCopy;
236  std::memcpy(MUserPtr, HostPtr.get(), MSizeInBytes);
237  };
238  } else {
239  setAlign(RequiredAlign);
240  MShadowCopy = allocateHostMem();
241  MUserPtr = MShadowCopy;
242  std::memcpy(MUserPtr, HostPtr.get(), MSizeInBytes);
243  }
244  }
245  }
246 
247  void handleHostData(const std::function<void(void *)> &CopyFromInput,
248  const size_t RequiredAlign, bool IsConstPtr) {
249  MHostPtrReadOnly = IsConstPtr;
250  setAlign(RequiredAlign);
251  if (useHostPtr())
252  throw runtime_error(
253  "Buffer constructor from a pair of iterator values does not support "
254  "use_host_ptr property.",
255  PI_ERROR_INVALID_OPERATION);
256 
257  setAlign(RequiredAlign);
258  MShadowCopy = allocateHostMem();
259  MUserPtr = MShadowCopy;
260 
261  CopyFromInput(MUserPtr);
262  }
263 
264  void setAlign(size_t RequiredAlign) {
265  MAllocator->setAlignment(RequiredAlign);
266  }
267 
268  static size_t getBufSizeForContext(const ContextImplPtr &Context,
269  pi_native_handle MemObject);
270 
271  void handleWriteAccessorCreation();
272 
273  void *allocateMem(ContextImplPtr Context, bool InitFromUserData,
274  void *HostPtr,
275  sycl::detail::pi::PiEvent &InteropEvent) override {
276  (void)Context;
277  (void)InitFromUserData;
278  (void)HostPtr;
279  (void)InteropEvent;
280  throw runtime_error("Not implemented", PI_ERROR_INVALID_OPERATION);
281  }
282 
283  MemObjType getType() const override { return MemObjType::Undefined; }
284 
285  ContextImplPtr getInteropContext() const override { return MInteropContext; }
286 
287  bool isInterop() const override;
288 
289  bool hasUserDataPtr() const override { return MUserPtr != nullptr; }
290 
291  bool isHostPointerReadOnly() const override { return MHostPtrReadOnly; }
292 
293  bool usesPinnedHostMemory() const override {
294  return has_property<
296  }
297 
298  void detachMemoryObject(const std::shared_ptr<SYCLMemObjT> &Self) const;
299 
300  void markAsInternal() { MIsInternal = true; }
301 
303  bool needsWriteBack() const { return MNeedWriteBack && MUploadDataFunctor; }
304 
307  void markBeingUsedInGraph() { MGraphUseCount += 1; }
308 
312  // Compare exchange loop to safely decrement MGraphUseCount
313  while (true) {
314  size_t CurrentVal = MGraphUseCount;
315  if (CurrentVal == 0) {
316  break;
317  }
318  if (MGraphUseCount.compare_exchange_strong(CurrentVal, CurrentVal - 1) ==
319  false) {
320  continue;
321  }
322  }
323  }
324 
326  bool isUsedInGraph() const { return MGraphUseCount > 0; }
327 
328 protected:
329  // An allocateMem helper that determines which host ptr to use
330  void determineHostPtr(const ContextImplPtr &Context, bool InitFromUserData,
331  void *&HostPtr, bool &HostPtrReadOnly);
332 
333  // Allocator used for allocation memory on host.
334  std::unique_ptr<SYCLMemObjAllocator> MAllocator;
335  // Properties passed by user.
337  // Event passed by user to interoperability constructor.
338  // Should wait on this event before start working with such memory object.
340  // Context passed by user to interoperability constructor.
342  // Native backend memory object handle passed by user to interoperability
343  // constructor.
345  // Indicates whether memory object is created using interoperability
346  // constructor or not.
348  // Indicates if user provided pointer is read only.
350  // Indicates if memory object should write memory to the host on destruction.
352  // Size of memory.
353  size_t MSizeInBytes;
354  // User's pointer passed to constructor.
355  void *MUserPtr;
356  // Copy of memory passed by user to constructor.
357  void *MShadowCopy;
358  // Function which update host with final data on memory object destruction.
359  std::function<void(void)> MUploadDataFunctor;
360  // Field which holds user's shared_ptr in case of memory object is created
361  // using constructor with shared_ptr.
362  std::shared_ptr<const void> MSharedPtrStorage;
363  // Field to identify if dtor is not necessarily blocking.
364  // check for MUploadDataFunctor is not enough to define it since for case when
365  // we have read only HostPtr - MUploadDataFunctor is empty but delayed release
366  // must be not allowed.
368  // Indicates that the memory object was allocated internally. Such memory
369  // objects can be released in a deferred manner regardless of whether a host
370  // pointer was provided or not.
371  bool MIsInternal = false;
372  // The number of graphs which are currently using this memory object.
373  std::atomic<size_t> MGraphUseCount = 0;
374  // Function which creates a shadow copy of the host pointer. This is used to
375  // defer the memory allocation and copying to the point where a writable
376  // accessor is created.
377  std::function<void(void)> MCreateShadowCopy = []() -> void {};
378  bool MOwnNativeHandle = true;
379 };
380 } // namespace detail
381 } // namespace _V1
382 } // namespace sycl
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:50
const std::unique_ptr< SYCLMemObjAllocator > & get_allocator_internal() const
ContextImplPtr getInteropContext() const override
__SYCL2020_DEPRECATED("get_count() is deprecated, please use size() instead") size_t get_count() const
void * allocateMem(ContextImplPtr Context, bool InitFromUserData, void *HostPtr, sycl::detail::pi::PiEvent &InteropEvent) override
virtual ~SYCLMemObjT()=default
bool has_property() const noexcept
void markNoLongerBeingUsedInGraph()
Decrement an internal counter for how many graphs are currently using this memory object.
size_t getSizeInBytes() const noexcept override
void handleHostData(const std::shared_ptr< void > &HostPtr, const size_t RequiredAlign, bool IsConstPtr)
void releaseHostMem(void *Ptr) override
SYCLMemObjT(const property_list &Props, std::unique_ptr< SYCLMemObjAllocator > Allocator)
bool needsWriteBack() const
Returns true if this memory object requires a write_back on destruction.
std::shared_ptr< const void > MSharedPtrStorage
MemObjType getType() const override
sycl::detail::pi::PiMem MInteropMemObject
bool hasUserDataPtr() const override
void addOrReplaceAccessorProperties(const property_list &PropertyList)
size_t size() const noexcept
void setAlign(size_t RequiredAlign)
SYCLMemObjT(cl_mem MemObject, const context &SyclContext, event AvailableEvent, std::unique_ptr< SYCLMemObjAllocator > Allocator)
void markBeingUsedInGraph()
Increment an internal counter for how many graphs are currently using this memory object.
bool canReadHostPtr(void *HostPtr, const size_t RequiredAlign)
SYCLMemObjT(const size_t SizeInBytes, const property_list &Props, std::unique_ptr< SYCLMemObjAllocator > Allocator)
std::function< void(void)> MUploadDataFunctor
void set_final_data(const std::function< void(const std::function< void(void *const Ptr)> &)> &FinalDataFunc)
bool isHostPointerReadOnly() const override
void set_final_data(std::nullptr_t)
void handleHostData(const void *HostPtr, const size_t RequiredAlign)
void deleteAccessorProperty(const PropWithDataKind &Kind)
void set_write_back(bool NeedWriteBack)
std::unique_ptr< SYCLMemObjAllocator > MAllocator
bool usesPinnedHostMemory() const override
bool isUsedInGraph() const
Returns true if any graphs are currently using this memory object.
void handleHostData(void *HostPtr, const size_t RequiredAlign)
void handleHostData(const std::function< void(void *)> &CopyFromInput, const size_t RequiredAlign, bool IsConstPtr)
bool canReuseHostPtr(void *HostPtr, const size_t RequiredAlign)
An event object can be used to synchronize memory transfers, enqueues of kernels and signaling barrie...
Definition: event.hpp:44
Objects of the property_list class are containers for the SYCL properties.
PiProgram cast(cl_program)=delete
std::shared_ptr< sycl::detail::context_impl > ContextImplPtr
Definition: event_impl.hpp:32
static const PluginPtr & getPlugin(backend Backend)
Definition: backend.cpp:32
std::shared_ptr< event_impl > EventImplPtr
Definition: cg.hpp:43
std::shared_ptr< plugin > PluginPtr
Definition: pi.hpp:48
static constexpr bool has_property()
class __SYCL_EBO __SYCL_SPECIAL_CLASS Dimensions
Definition: access.hpp:18
uintptr_t pi_native_handle
Definition: pi.h:217
_pi_image_channel_type
Definition: pi.h:658
_pi_image_channel_order
Definition: pi.h:640
_Abi const simd< _Tp, _Abi > & noexcept
Definition: simd.hpp:1324