HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CE_Context.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: CE_Context.h ( CE Library, C++)
7  *
8  * COMMENTS: Compute Engine Contexts.
9  */
10 
11 #ifndef __CE_Context__
12 #define __CE_Context__
13 
14 #include "CE_API.h"
15 #include "CE_Tracing.h"
16 #include "CE_Error.h"
17 
18 #include <UT/UT_Array.h>
19 #include <UT/UT_Error.h>
20 #include <UT/UT_Lock.h>
21 #include <UT/UT_Map.h>
22 #include <UT/UT_NonCopyable.h>
23 #include <UT/UT_StringMap.h>
24 #include <UT/UT_UniquePtr.h>
25 #include <SYS/SYS_Types.h>
26 #include <SYS/SYS_Handle.h>
27 #include <iosfwd>
28 
29 class CE_MemoryPool;
30 class UT_MemoryResource;
31 
32 typedef void (*CE_ErrorCB)(const char *errmsg, UT_ErrorSeverity severity,
33  void *data);
34 
36 {
37 public:
40 
42 
43  virtual void rebindOGLBuffer( uint buf_obj ) = 0;
44  virtual void unbindOGLBuffer() = 0;
45  virtual bool isBinded() = 0;
46 };
47 
48 /// An OpenCL buffer that is backed by a external memory. External memory is accessed
49 /// through a platform specific memory handle.
50 /// Requires the OpenCL driver implements the following extensions:
51 /// cl_khr_external_memory
52 /// cl_khr_external_memory_opaque_fd (only on Linux and possibly Mac)
53 /// cl_khr_external_memory_win32 (only on Windows)
54 class CE_API CE_ExternalBuffer : public cl::Buffer
55 {
56 public:
58  const cl::Context &context,
60  ::size_t size,
61  SYS_Handle handle,
62  cl_int *err = nullptr);
63 
64  CE_ExternalBuffer() : cl::Buffer() {}
65 };
66 /// An OpenCL image that is backed by external memory, similar to
67 /// CE_ExternalBuffer.
69 {
70 public:
72  const cl::Context &context,
74  const cl_image_format *format,
75  const cl_image_desc *desc,
76  SYS_Handle handle,
77  cl_int *err = nullptr);
78 
79  CE_ExternalImage() : cl::Image() {}
80 };
81 
82 #ifndef CL_UUID_SIZE_KHR
83 #define CL_UUID_SIZE_KHR 16
84 #endif
85 
86 /// CE_Context encapsulates the OpenCL context and provides various convenience
87 /// functions for loading kernel programs and allocating GPU memory.
89 {
90 public:
91  CE_Context();
92  virtual ~CE_Context();
93 
95 
96  /// Returns a pointer to the singleton CE_Context object. This function
97  /// attempts to initialize OpenCL if it has not yet been.
98  /// gl_shared should be true if the context will be expected to interoperate
99  /// with the OpenGL context. If both gl_shared and shared_fallback are true,
100  /// then the function will try to make an unshared context in case the
101  /// shared context fails to create.
102  static CE_Context *getContext(bool gl_shared = true,
103  bool shared_fallback = true);
104  /// Returns true if interoperability between CL and GL is possible.
105  static bool isGLSharingPossible();
106 
107  /// Returns true if interoperability between CL and VK is possible.
108  bool isSameDevice(const uint8 (&uuid)[16]);
109 
110  /// Returns the underlying cl::Context object.
111  cl::Context getCLContext() const {return myContext;}
112 
113  /// Returns the cl::Queue object that is used to enqueue OpenCL kernels
114  /// and memory transfers.
115  cl::CommandQueue getQueue() const {return myQueue;}
116 
117  /// Returns the OpenCL Device object.
118  cl::Device getDevice() const {return myDevice;}
119 
120  ceTraceCtx getTraceContext() const {return myTraceCtx;}
121 
122  // Write OpenCL Device info to the supplied buffer.
123  static void getInfo(const cl::Device &device, UT_WorkBuffer &buffer );
124  static void getExtendedInfo(const cl::Device &device, UT_WorkBuffer &buffer );
125 
126  // Write info for all available OpenCL platforms to the supplied buffer.
127  static void getAllPlatformsInfo(UT_WorkBuffer &buffer);
128 
129  /// Get the suggested global and local ranges for the given 1-D kernel over
130  /// the specified number of items.
131  void get1DRanges(const cl::Kernel &k, size_t items,
132  cl::NDRange &g, cl::NDRange &l);
133 
134  /// Get the suggested global and local ranges for a reduction for the
135  /// given 1-D kernel over the specified number of items.
136  void getReductionRanges(const cl::Kernel &k, size_t items, size_t accumsize,
137  cl::NDRange &global_range, cl::NDRange &local_range,
138  uint &groupsize, uint &ngroups) const;
139 
140  /// Get the maximum workgroup size for the given kernel.
141  size_t getMaxWorkgroupSize(const cl::Kernel &k);
142  /// Get the array of maximum work items along each dimension supported by the
143  /// compute device.
144  std::vector<size_t> getMaxWorkItemSizes();
145 
146  /// Round up a provided group size to a larger clean one as some
147  /// driers die with prime-based groups sizes.
148  /// Less than 1024 is raised to next power of 2, greater is to a multiple
149  /// of 1024.
150  static size_t roundUpGroupSize(size_t gsize);
151 
152  /// Round up a 2D group size to something that can factor nicely.
153  /// Same rule as roundUpGroupSize of ensuring there is a 1024 factor
154  /// present if group greater than 1024, else power of 2.
155  static void roundUpGroupSize2D(exint &width, exint &height);
156 
157  /// Loads the OpenCL program specified by progname. This functions searches
158  /// for the file in the HOUDINI_OCL_PATH environment variable. Any compile-
159  /// time options can be passed in the options parameter. If the program
160  /// load succeeds, the progname will be cached, using the progrname and
161  /// options strings together as a hash value lookup. In this way the same
162  /// OpenCL program can be loaded several times with different compile-time
163  /// flags.
164  cl::Program loadProgram(const char *progname,
165  const char *options = nullptr,
166  bool recompile = false);
167 
168  /// Like loadProgram() except it creates a program from code directly.
169  /// `progname` should be provided where possible so that errors are easier
170  /// to find.
171  cl::Program compileProgram(const char *progtext,
172  const char *options = nullptr,
173  bool recompile = false,
174  const char *progname = nullptr);
175 
176  /// Create an OpenCL kernel named kernelname from the program specified by
177  /// progname. For some types of devices these kernels will be cached, as
178  /// kernels can be expensive to create. This is the recommended method
179  /// for creating kernels.
180  cl::Kernel loadKernel(const cl::Program &prog, const UT_StringRef &kernelname);
181  cl::Kernel loadKernel(const char *progname, const UT_StringRef &kernelname,
182  const char *options = nullptr)
183  { return loadKernel(loadProgram(progname, options), kernelname); }
184 
185  /// Returns whether the CE_Context has been successfully initialized.
186  bool isValid() const {return myIsValid;}
187 
188  /// Returns whether the singleton CE_Context has been initialized yet. This
189  /// can be used to test whether OpenCL has been initialized without calling
190  /// getContext and forcing an attempt at initialization.
191  static bool isInitialized(bool gl_shared=false);
192 
193  /// Returns true if the OpenCL device is running on the CPU.
194  bool isCPU() const;
195 
196  /// Returns true if the OpenCL device supports double precision.
197  bool hasDoubleSupport() const {return mySupportsDouble;}
198  /// Returns true if the OpenCL device supports writing to 3D image objects.
199  bool has3DImageWriteSupport() const {return mySupports3DImageWrites;}
200 
201  /// Block until any outstanding kernel or memory transfers on the main
202  /// CommandQueue have executed. If sweep_pool is true, the context's
203  /// CE_MemoryPool will sweep for any buffers that were in use when their
204  /// CE_Grid's went out of scope, but that were still active in kernels.
205  void finish(bool sweep_pool=true);
206 
207  /// Allocate a buffer of specified size on the CE_Device.
208  /// use_pool= true, attempts to use the underlying CE_MemoryPool to possibly return
209  /// an already allocated, unused buffer.
210  /// read=true, creates a buffer that is readable inside kernels.
211  /// write=true, creates a buffer that is writable inside kernels.
212  /// ogl_bind, specifies an OGL buffer to bind to.
213  cl::Buffer allocBuffer(int64 size, bool use_pool=true, bool read=true, bool write=true, uint32 ogl_bind=SYS_UINT32_MAX);
214 
215  /// Release the specified buffer, possibly to the CE_MemoryPool.
216  void releaseBuffer(cl::Buffer &&buf, bool use_pool=true);
217 
218  /// Read the specified number of bytes from the buffer.
219  void readBuffer(const cl::Buffer &buf, size_t size, void *p, bool blocking = true, size_t offset = 0);
220 
221  /// Write the specified number of bytes to the buffer.
222  void writeBuffer(const cl::Buffer &buf, size_t size, const void *p, bool blocking = true, size_t offset = 0);
223 
224  /// Copy the specified amount of data from source buffer to destination
225  /// buffer.
226  void copyBuffer(const cl::Buffer &src, const cl::Buffer &dst, size_t size,
227  size_t src_offset = 0, size_t dst_offset = 0);
228 
229  /// Copy data from the source buffer into the specified region of the
230  /// destination image.
231  void copyBufferToImage(const cl::Buffer &src, const cl::Image &dst,
232  const cl::size_t<3>& dst_origin,
233  const cl::size_t<3>& dst_region,
234  size_t src_offset = 0);
235 
236  /// Copy data from the specified region of the source image to the
237  /// destination buffer.
238  void copyImageToBuffer(const cl::Image &src, const cl::Buffer &dst,
239  const cl::size_t<3>& src_origin,
240  const cl::size_t<3>& src_region,
241  size_t dst_offset = 0);
242 
243  /// Enqueue the kernel over the provided ranges.
244  void enqueueKernel(const cl::Kernel &kernel, const cl::NDRange &global, const cl::NDRange &local);
245 
246  /// Keep a map buffer to bind at render time
247  /// The first time a CL::Buffer is created it can be registered to rebing to a OGL vertex buffer at drawing time.
248  /// The uint returned by the register call can be attached to a detail attribute and the drawing code can convert
249  /// the CL Buffer to a CL BufferGL.
250  uint32 registerDelayedOGLBindBuffer(CE_DelayedOGLBindBuffer* buffer);
251  void unregisterDelayedOGLBindBuffer(uint32 id);
252  CE_DelayedOGLBindBuffer* lookupDelayedOGLBindBuffer( uint id );
253 
254  /// Returns true if the context supports querying for device and driver
255  /// UUIDs that are unique across APIs (allowing for example, to match Vulkan
256  /// device selection). This requires the cl_khr_device_uuid extension.
257  bool supportsUUID();
258  /// Writes the OpenCL device UUID. Check that supportsUUID() returns true
259  /// before trying to query the UUID.
260  void getDeviceUUID(cl_uchar (&uuid)[CL_UUID_SIZE_KHR]);
261  /// Writes the OpenCL driver UUID. Check that supportsUUID() returns true
262  /// before trying to query the UUID.
263  void getDriverUUID(cl_uchar (&uuid)[CL_UUID_SIZE_KHR]);
264 
265  /// Returns true if the context supports the creation of buffers backed by
266  /// external memory. Mainly for use in sharing buffers with Vulkan.
267  bool supportsExternalMemory();
268 
269  /// Create a buffer backed by external memory. The handle will be the reference
270  /// to the actual memory object, and its lifetime is externally managed. The
271  /// memory might be owned by another GPU API, such as Vulkan. In those cases,
272  /// that handle can be obtained by a call to
273  /// vkGetMemoryFdKHR or vkGetMemoryWin32HandleKHR
274  /// This requires a few extensions to be available. Check that the context
275  /// supports these by calling CE_Context::supportsExternalMemory first.
276  CE_ExternalBuffer createExternalMemoryBuffer(SYS_Handle handle, int64_t size, bool read=true, bool write=true);
277 
278  CE_ExternalImage createExternalImage(SYS_Handle handle,
279  const cl_image_format& format,
280  const cl_image_desc& image_desc,
281  bool read = true, bool write = true);
282 
283  /// Clear the CE_MemoryPool object.
284  void clearMemoryPool();
285 
286  /// Sweep the memory pool for in use buffers. We call this on every finish
287  /// call as well, but if we have a large sequence of operations that use
288  /// temporary buffers without a finish, it's a good idea to call this directly
289  /// occasionally.
290  void sweepInUseMemory();
291 
292  /// Return a pointer to pinned (page-locked) host memory. On some devices
293  /// (Nvidia), using this type of memory for the PCI/E host/device transfers
294  /// can double the throughput. Will return NULL if the memory can't be
295  /// allocated, or if the device is not a GPU.
296  fpreal32 *getPinnedBuffer(int64 size);
297 
298  cl::Buffer getXNoiseData();
299 
300  /// Standard error reporting for OpenCL exceptions. They should generally
301  /// take the form:
302  /// @code
303  /// try
304  /// {
305  /// OpenCL calls...
306  /// }
307  /// catch(cl::Error &err)
308  /// {
309  /// CE_Context::reportError(err);
310  /// ///cleanup
311  /// }
312  /// @endcode
313  /// This will not capture delayed errors, however. Instead
314  /// you will need to add a callback to intercept them.
315  static void reportError(const cl::Error &err);
316  static void outputErrorMessage(const char *err_msg);
317  static void setErrorCB(CE_ErrorCB callback, void *data);
318  static void outputWarningMessage(const char *err_msg);
319 
320  static void initMainSharedGLContext( int devicetype, void* context, void* display, const cl_uchar (&uuid)[16]);
321  static bool useHalfNormalDelayedBindBuffer();
322 
323  /// Marks that an operation has run out of memory, allowing us
324  /// to report elsewhere.
325  void setOutOfMemoryFailure(bool hasfailed = true) { myOutOfMemoryFailure = true; }
326  bool hasOutOfMemoryFailureHappened() const { return myOutOfMemoryFailure; }
327 
328  /// Combination of environment variable, but overriden to be false by known buggy drivers
329  static bool shouldUseOCLOGLInterop();
330 
331  /// This structure holds a device name, vendor, device number, and drive version with respect to
332  /// its vendor platform.
334  {
338  int number;
340  };
341  /// Get the vector of available devices of the given type.
342  static void getDevices(UT_Array<DeviceDescriptor>&, cl_device_type t);
343 
344  /// Get an index to the preferred/default device for the specified device
345  /// type and the list of available devices.
346  static int getDefaultDevice(
348 
349  /// Returns true if environment variables are set that override preferences.
350  static bool isEnvironmentOverride();
351 
352  // Queries the device by calling clGetDeviceInfo, but returning false and setting
353  // result to zero for unknown flags or flags that are disabled with environment
354  // variables.
355  template <class T>
356  static bool getDeviceInfoRestricted(cl_device_id device, cl_uint flag, T &result);
357 
358  /// Queries the current device give the specified flag using clGetDeviceInfo,
359  /// used by ocldeviceinfo EXPR function. Returns false for unrecognized flag.
360  bool getDeviceInfo(const char *flag, fpreal &result);
361 
362  /// This function returns the total size of addressable compute memory for
363  /// the current device.
364  size_t getAddressableMemory() const;
365 
366  /// Report memory usage
367  void reportUsage(std::ostream &os) const;
368 
369  /// Requests the specified amount of memory to be freed from the OpenCL
370  /// device and returns the amount actually freed.
371  int64 requestMemoryFromResource(int64 amount);
372  /// Returns the memory resource object for the OpenCL device. All objects
373  /// that use OpenCL memory should ideally be registered as clients for this
374  /// resource. NOTE: this only exists if the context is valid!
376  {
377  return myMemResource;
378  }
379 
380 private:
381  UT_UniquePtr<cl::Program> doCompileProgram(
382  const char *progname,
383  const char *progtext,
384  const char *options,
385  bool recompile);
386 
387  /// Initialize the context for the given device.
388  void init(cl::Context &context, cl::Device &device, bool gl_shared);
389 
390  /// Releases the pinned, page-locked memory buffer.
391  void releasePinnedBuffer();
392 
393 
394  cl::Context myContext;
395  cl::CommandQueue myQueue;
396  cl::CommandQueue myDeviceQueue;
397  ceTraceCtx myTraceCtx;
398  cl::Device myDevice;
399  bool myIsValid;
400  bool mySupportsDouble;
401  bool mySupports3DImageWrites;
402  cl::Buffer myXNoiseData;
403  UT_Lock myXNoiseLock;
404 
405  // Indirection to avoid changing reference counts when caches resize/rehash.
406  using ProgramUPtr = UT_UniquePtr<cl::Program>;
407  using KernelUPtr = UT_UniquePtr<cl::Kernel>;
408 
409  struct KernelInfo
410  {
412  UT_Array<KernelUPtr> kernels;
413  };
414 
415  UT_StringMap<ProgramUPtr> myProgramTable;
417  UT_Lock myKernelCacheLock;
418 
419  UT_UniquePtr<CE_MemoryPool> myMemPool;
420  UT_MemoryResource *myMemResource;
421 
422  // The pinned buffer is unique to the main thread.
423  cl::Buffer myPinnedBuffer;
424  fpreal32 *myPinnedData;
425 
426  bool myOutOfMemoryFailure;
427 
428  UT_Map<uint32,CE_DelayedOGLBindBuffer*> myDelayedOGLBindBuffers;
429 
430  static void* theGLContext;
431  static void* theGLDisplay;
432  static int theGLDeviceType;
433  static char theGLDeviceUUID[16];
434 };
435 
436 /// NOTE: this function will retry if it fails on allocation failure, after
437 /// freeing some memory.
439 ce_enqueueKernel(const cl::CommandQueue& queue, const cl::Kernel &kernel,
440  const cl::NDRange &offset, const cl::NDRange &global, const cl::NDRange &local,
441  const std::vector<cl::Event>* events,
442  cl::Event* event);
443 
444 #endif
#define CE_API
Definition: CE_API.h:13
struct _cl_device_id * cl_device_id
Definition: cl.h:30
uint32_t cl_uint
Definition: cl_platform.h:261
bool has3DImageWriteSupport() const
Returns true if the OpenCL device supports writing to 3D image objects.
Definition: CE_Context.h:199
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glcorearb.h:2540
GLbitfield flags
Definition: glcorearb.h:1596
cl::Device getDevice() const
Returns the OpenCL Device object.
Definition: CE_Context.h:118
Unsorted map container.
Definition: UT_Map.h:114
*get result *(waiting if necessary)*A common idiom is to fire a bunch of sub tasks at the and then *wait for them to all complete We provide a helper class
Definition: thread.h:632
void
Definition: png.h:1083
GLboolean * data
Definition: glcorearb.h:131
GLboolean GLboolean g
Definition: glcorearb.h:1222
UT_StringHolder platformVendor
Definition: CE_Context.h:337
int64 exint
Definition: SYS_Types.h:125
UT_ErrorSeverity
Definition: UT_Error.h:25
int32_t cl_int
Definition: cl_platform.h:260
uint8_t cl_uchar
Definition: cl_platform.h:257
ceTraceCtx getTraceContext() const
Definition: CE_Context.h:120
CE_API cl_int ce_enqueueKernel(const cl::CommandQueue &queue, const cl::Kernel &kernel, const cl::NDRange &offset, const cl::NDRange &global, const cl::NDRange &local, const std::vector< cl::Event > *events, cl::Event *event)
**But if you need a result
Definition: thread.h:622
virtual ~CE_DelayedOGLBindBuffer()
Definition: CE_Context.h:39
Definition: Image.h:45
float fpreal32
Definition: SYS_Types.h:200
void * ceTraceCtx
Definition: CE_Tracing.h:58
GLuint buffer
Definition: glcorearb.h:660
cl::CommandQueue getQueue() const
Definition: CE_Context.h:115
#define CL_UUID_SIZE_KHR
Definition: CE_Context.h:83
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
struct _cl_event * event
Definition: glcorearb.h:2961
unsigned char uint8
Definition: SYS_Types.h:36
Event interface for cl_event.
Definition: cl.hpp:1647
GLint GLsizei GLsizei height
Definition: glcorearb.h:103
bool isValid() const
Returns whether the CE_Context has been successfully initialized.
Definition: CE_Context.h:186
UT_StringHolder driverVersion
Definition: CE_Context.h:339
GLintptr offset
Definition: glcorearb.h:665
cl_bitfield cl_device_type
Definition: cl.h:42
GLuint writeBuffer
Definition: glcorearb.h:2674
GLint GLint GLsizei GLint GLenum format
Definition: glcorearb.h:108
#define UT_NON_COPYABLE(CLASS)
Define deleted copy constructor and assignment operator inside a class.
*get result *(waiting if necessary)*A common idiom is to fire a bunch of sub tasks at the queue
Definition: thread.h:632
long long int64
Definition: SYS_Types.h:116
void setOutOfMemoryFailure(bool hasfailed=true)
Definition: CE_Context.h:325
GLenum GLenum severity
Definition: glcorearb.h:2539
GLuint const GLchar * name
Definition: glcorearb.h:786
#define SYS_UINT32_MAX
Definition: SYS_Types.h:172
GLdouble t
Definition: glad.h:2397
GLsizeiptr size
Definition: glcorearb.h:664
GLenum GLenum dst
Definition: glcorearb.h:1793
CommandQueue interface for cl_command_queue.
Definition: cl.hpp:2852
cl_int getInfo(Func f, cl_uint name, T *param)
Definition: cl.hpp:1030
cl::Kernel loadKernel(const char *progname, const UT_StringRef &kernelname, const char *options=nullptr)
Definition: CE_Context.h:181
fpreal64 fpreal
Definition: SYS_Types.h:283
UT_MemoryResource * getMemoryResource() const
Definition: CE_Context.h:375
Base class interface for all images.
Definition: cl.hpp:2098
unsigned int uint32
Definition: SYS_Types.h:40
Memory buffer interface.
Definition: cl.hpp:1867
GLint GLsizei width
Definition: glcorearb.h:103
NDRange interface.
Definition: cl.hpp:2466
void(* CE_ErrorCB)(const char *errmsg, UT_ErrorSeverity severity, void *data)
Definition: CE_Context.h:32
Kernel interface that implements cl_kernel.
Definition: cl.hpp:2544
cl::Context getCLContext() const
Returns the underlying cl::Context object.
Definition: CE_Context.h:111
Device interface for cl_device_id.
Definition: cl.hpp:1265
bool hasDoubleSupport() const
Returns true if the OpenCL device supports double precision.
Definition: CE_Context.h:197
Program interface that implements cl_program.
Definition: cl.hpp:2649
bool hasOutOfMemoryFailureHappened() const
Definition: CE_Context.h:326
unsigned int uint
Definition: SYS_Types.h:45
cl_bitfield cl_mem_flags
Definition: cl.h:66
Definition: format.h:1821
GLenum src
Definition: glcorearb.h:1793