VTK-m  2.0
cuda/internal/TransferToOpenGL.h
Go to the documentation of this file.
1 //============================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //============================================================================
10 #ifndef vtkm_interop_cuda_internal_TransferToOpenGL_h
11 #define vtkm_interop_cuda_internal_TransferToOpenGL_h
12 
15 
17 
19 
20 // Disable warnings we check vtkm for but Thrust does not.
22 VTKM_THIRDPARTY_PRE_INCLUDE
23 #include <thrust/copy.h>
24 #include <thrust/device_ptr.h>
25 #include <thrust/system/cuda/execution_policy.h>
26 #include <vtkm/exec/cuda/internal/ExecutionPolicy.h>
27 VTKM_THIRDPARTY_POST_INCLUDE
28 
29 namespace vtkm
30 {
31 namespace interop
32 {
33 namespace internal
34 {
35 
41 class CudaTransferResource : public vtkm::interop::internal::TransferResource
42 {
43 public:
44  CudaTransferResource()
45  : vtkm::interop::internal::TransferResource()
46  {
47  this->Registered = false;
48  }
49 
50  ~CudaTransferResource()
51  {
52  //unregister the buffer
53  if (this->Registered)
54  {
55  cudaGraphicsUnregisterResource(this->CudaResource);
56  }
57  }
58 
59  bool IsRegistered() const { return Registered; }
60 
61  void Register(GLuint* handle)
62  {
63  if (this->Registered)
64  {
65  //If you don't release the cuda resource before re-registering you
66  //will leak memory on the OpenGL side.
67  cudaGraphicsUnregisterResource(this->CudaResource);
68  }
69 
70  this->Registered = true;
71  cudaError_t cError =
72  cudaGraphicsGLRegisterBuffer(&this->CudaResource, *handle, cudaGraphicsMapFlagsWriteDiscard);
73  if (cError != cudaSuccess)
74  {
75  throw vtkm::cont::ErrorExecution("Could not register the OpenGL buffer handle to CUDA.");
76  }
77  }
78 
79  void Map()
80  {
81  //map the resource into cuda, so we can copy it
82  cudaError_t cError = cudaGraphicsMapResources(1, &this->CudaResource, cudaStreamPerThread);
83  if (cError != cudaSuccess)
84  {
86  "Could not allocate enough memory in CUDA for OpenGL interop.");
87  }
88  }
89 
90  template <typename ValueType>
91  ValueType* GetMappedPoiner(vtkm::Int64 desiredSize)
92  {
93  //get the mapped pointer
94  std::size_t cuda_size;
95  ValueType* pointer = nullptr;
96  cudaError_t cError =
97  cudaGraphicsResourceGetMappedPointer((void**)&pointer, &cuda_size, this->CudaResource);
98 
99  if (cError != cudaSuccess)
100  {
101  throw vtkm::cont::ErrorExecution("Unable to get pointers to CUDA memory for OpenGL buffer.");
102  }
103 
104  //assert that cuda_size is the same size as the buffer we created in OpenGL
105  VTKM_ASSERT(cuda_size >= static_cast<std::size_t>(desiredSize));
106  return pointer;
107  }
108 
109  void UnMap() { cudaGraphicsUnmapResources(1, &this->CudaResource, cudaStreamPerThread); }
110 
111 private:
112  bool Registered;
113  cudaGraphicsResource_t CudaResource;
114 };
115 
121 template <typename ValueType>
122 class TransferToOpenGL<ValueType, vtkm::cont::DeviceAdapterTagCuda>
123 {
124  using DeviceAdapterTag = vtkm::cont::DeviceAdapterTagCuda;
125 
126 public:
127  VTKM_CONT explicit TransferToOpenGL(BufferState& state)
128  : State(state)
129  , Resource(nullptr)
130  {
131  if (!this->State.HasType())
132  {
133  this->State.DeduceAndSetType(ValueType());
134  }
135 
136  this->Resource =
137  dynamic_cast<vtkm::interop::internal::CudaTransferResource*>(state.GetResource());
138  if (!this->Resource)
139  {
140  vtkm::interop::internal::CudaTransferResource* cudaResource =
141  new vtkm::interop::internal::CudaTransferResource();
142 
143  //reset the resource to be a cuda resource
144  this->State.SetResource(cudaResource);
145  this->Resource = cudaResource;
146  }
147  }
148 
149  template <typename StorageTag>
150  VTKM_CONT void Transfer(const vtkm::cont::ArrayHandle<ValueType, StorageTag>& handle) const
151  {
152  //make a buffer for the handle if the user has forgotten too
153  if (!glIsBuffer(*this->State.GetHandle()))
154  {
155  glGenBuffers(1, this->State.GetHandle());
156  }
157 
158  //bind the buffer to the given buffer type
159  glBindBuffer(this->State.GetType(), *this->State.GetHandle());
160 
161  //Determine if we need to reallocate the buffer
162  const vtkm::Int64 size =
163  static_cast<vtkm::Int64>(sizeof(ValueType)) * handle.GetNumberOfValues();
164  this->State.SetSize(size);
165  const bool resize = this->State.ShouldRealloc(size);
166  if (resize)
167  {
168  //Allocate the memory and set it as GL_DYNAMIC_DRAW draw
169  glBufferData(this->State.GetType(), static_cast<GLsizeiptr>(size), 0, GL_DYNAMIC_DRAW);
170 
171  this->State.SetCapacity(size);
172  }
173 
174  if (!this->Resource->IsRegistered() || resize)
175  {
176  //register the buffer as being used by cuda. This needs to be done everytime
177  //we change the size of the buffer. That is why we only change the buffer
178  //size as infrequently as possible
179  this->Resource->Register(this->State.GetHandle());
180  }
181 
182  this->Resource->Map();
183 
184  ValueType* beginPointer = this->Resource->GetMappedPoiner<ValueType>(size);
186  beginPointer, handle.GetNumberOfValues(), DeviceAdapterTag{}, [](void*) {});
187 
188  //Do a device to device memory copy
190 
191  //unmap the resource
192  this->Resource->UnMap();
193  }
194 
195 private:
197  vtkm::interop::internal::CudaTransferResource* Resource;
198 };
199 }
200 }
201 } //namespace vtkm::interop::cuda::internal
202 
203 #endif
vtkm::cont::ArrayHandle::GetNumberOfValues
VTKM_CONT vtkm::Id GetNumberOfValues() const
Returns the number of entries in the array.
Definition: ArrayHandle.h:448
vtkm::interop::TransferToOpenGL
VTKM_CONT void TransferToOpenGL(const vtkm::cont::ArrayHandle< ValueType, StorageTag > &handle, BufferState &state, DeviceAdapterTag)
Manages transferring an ArrayHandle to opengl .
Definition: TransferToOpenGL.h:41
ErrorBadAllocation.h
vtkm::cont::ArrayHandle
Manages an array-worth of data.
Definition: ArrayHandle.h:283
vtkm::interop::BufferState
Manages the state for transferring an ArrayHandle to opengl.
Definition: BufferState.h:56
vtkm::cont::DeviceAdapterAlgorithm::Copy
static VTKM_CONT void Copy(const vtkm::cont::ArrayHandle< T, CIn > &input, vtkm::cont::ArrayHandle< U, COut > &output)
Copy the contents of one ArrayHandle to another.
vtkm
Groups connected points that have the same field value.
Definition: Atomic.h:19
VTKM_ASSERT
#define VTKM_ASSERT(condition)
Definition: Assert.h:43
ThrustPatches.h
DeviceAdapterTagCuda.h
VTKM_CONT
#define VTKM_CONT
Definition: ExportMacros.h:57
ErrorExecution.h
vtkm::cont::ErrorBadAllocation
This class is thrown when VTK-m attempts to manipulate memory that it should not.
Definition: ErrorBadAllocation.h:25
vtkm::cont::ArrayHandleBasic
Definition: ArrayHandleBasic.h:97
TransferToOpenGL.h
vtkm::cont::ErrorExecution
This class is thrown in the control environment whenever an error occurs in the execution environment...
Definition: ErrorExecution.h:25