TransferToOpenGL.h 5.9 KB
Newer Older
1
2
3
4
//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
5
//
6
7
8
9
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//============================================================================
10
11
#ifndef vtkm_interop_cuda_internal_TransferToOpenGL_h
#define vtkm_interop_cuda_internal_TransferToOpenGL_h
12

David C. Lonie's avatar
David C. Lonie committed
13
#include <vtkm/cont/ErrorBadAllocation.h>
14
#include <vtkm/cont/ErrorExecution.h>
15
16
17

#include <vtkm/cont/cuda/internal/DeviceAdapterTagCuda.h>

18
#include <vtkm/interop/internal/TransferToOpenGL.h>
19

20
// Disable warnings we check vtkm for but Thrust does not.
21
#include <vtkm/exec/cuda/internal/ThrustPatches.h>
22
VTKM_THIRDPARTY_PRE_INCLUDE
23
24
#include <thrust/copy.h>
#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
30
31
32
33
34
namespace vtkm
{
namespace interop
{
namespace internal
{
35

36
/// \brief cuda backend and opengl interop resource management
37
///
38
/// \c TransferResource manages cuda resource binding for a given buffer
39
///
40
///
41
class CudaTransferResource : public vtkm::interop::internal::TransferResource
42
43
{
public:
44
45
  CudaTransferResource()
    : vtkm::interop::internal::TransferResource()
46
  {
47
48
    this->Registered = false;
  }
49

50
51
52
  ~CudaTransferResource()
  {
    //unregister the buffer
53
    if (this->Registered)
54
    {
55
      cudaGraphicsUnregisterResource(this->CudaResource);
56
    }
57
  }
58

59
  bool IsRegistered() const { return Registered; }
60

61
62
  void Register(GLuint* handle)
  {
63
64
    if (this->Registered)
    {
65
66
67
      //If you don't release the cuda resource before re-registering you
      //will leak memory on the OpenGL side.
      cudaGraphicsUnregisterResource(this->CudaResource);
68
    }
69
70

    this->Registered = true;
71
72
73
    cudaError_t cError =
      cudaGraphicsGLRegisterBuffer(&this->CudaResource, *handle, cudaGraphicsMapFlagsWriteDiscard);
    if (cError != cudaSuccess)
74
    {
75
      throw vtkm::cont::ErrorExecution("Could not register the OpenGL buffer handle to CUDA.");
76
    }
77
  }
78

79
80
81
  void Map()
  {
    //map the resource into cuda, so we can copy it
82
83
    cudaError_t cError = cudaGraphicsMapResources(1, &this->CudaResource);
    if (cError != cudaSuccess)
84
    {
David C. Lonie's avatar
David C. Lonie committed
85
      throw vtkm::cont::ErrorBadAllocation(
86
        "Could not allocate enough memory in CUDA for OpenGL interop.");
87
    }
88
  }
89

90
91
  template <typename ValueType>
  ValueType* GetMappedPoiner(vtkm::Int64 desiredSize)
92
  {
93
94
95
96
97
    //get the mapped pointer
    std::size_t cuda_size;
    ValueType* pointer = nullptr;
    cudaError_t cError =
      cudaGraphicsResourceGetMappedPointer((void**)&pointer, &cuda_size, this->CudaResource);
98

99
100
101
102
    if (cError != cudaSuccess)
    {
      throw vtkm::cont::ErrorExecution("Unable to get pointers to CUDA memory for OpenGL buffer.");
    }
103

104
105
106
    //assert that cuda_size is the same size as the buffer we created in OpenGL
    VTKM_ASSERT(cuda_size >= static_cast<std::size_t>(desiredSize));
    return pointer;
107
108
  }

109
110
  void UnMap() { cudaGraphicsUnmapResources(1, &this->CudaResource); }

111
112
113
114
115
116
117
118
119
120
private:
  bool Registered;
  cudaGraphicsResource_t CudaResource;
};

/// \brief Manages transferring an ArrayHandle to opengl .
///
/// \c TransferToOpenGL manages to transfer the contents of an ArrayHandle
/// to OpenGL as efficiently as possible.
///
121
template <typename ValueType>
122
123
class TransferToOpenGL<ValueType, vtkm::cont::DeviceAdapterTagCuda>
{
124
  using DeviceAdapterTag = vtkm::cont::DeviceAdapterTagCuda;
125

126
public:
127
128
129
  VTKM_CONT explicit TransferToOpenGL(BufferState& state)
    : State(state)
    , Resource(nullptr)
130
  {
131
    if (!this->State.HasType())
132
    {
133
      this->State.DeduceAndSetType(ValueType());
134
135
    }

136
137
138
    this->Resource =
      dynamic_cast<vtkm::interop::internal::CudaTransferResource*>(state.GetResource());
    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
146
147
148
      this->Resource = cudaResource;
    }
  }

149
  template <typename StorageTag>
150
  VTKM_CONT void Transfer(const vtkm::cont::ArrayHandle<ValueType, StorageTag>& handle) const
151
  {
152
153
154
155
156
    //make a buffer for the handle if the user has forgotten too
    if (!glIsBuffer(*this->State.GetHandle()))
    {
      glGenBuffers(1, this->State.GetHandle());
    }
157

158
159
    //bind the buffer to the given buffer type
    glBindBuffer(this->State.GetType(), *this->State.GetHandle());
160

161
162
163
164
165
166
167
168
169
    //Determine if we need to reallocate the buffer
    const vtkm::Int64 size =
      static_cast<vtkm::Int64>(sizeof(ValueType)) * handle.GetNumberOfValues();
    this->State.SetSize(size);
    const bool resize = this->State.ShouldRealloc(size);
    if (resize)
    {
      //Allocate the memory and set it as GL_DYNAMIC_DRAW draw
      glBufferData(this->State.GetType(), static_cast<GLsizeiptr>(size), 0, GL_DYNAMIC_DRAW);
170

171
172
      this->State.SetCapacity(size);
    }
173

174
175
176
177
178
179
180
    if (!this->Resource->IsRegistered() || resize)
    {
      //register the buffer as being used by cuda. This needs to be done everytime
      //we change the size of the buffer. That is why we only change the buffer
      //size as infrequently as possible
      this->Resource->Register(this->State.GetHandle());
    }
181

182
    this->Resource->Map();
183

184
    ValueType* beginPointer = this->Resource->GetMappedPoiner<ValueType>(size);
185
    auto deviceMemory = vtkm::cont::make_ArrayHandle(beginPointer, size);
186

187
188
    //Do a device to device memory copy
    vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapterTag>::Copy(handle, deviceMemory);
189

190
191
    //unmap the resource
    this->Resource->UnMap();
192
  }
193

194
private:
195
196
  vtkm::interop::BufferState& State;
  vtkm::interop::internal::CudaTransferResource* Resource;
197
198
199
};
}
}
200
} //namespace vtkm::interop::cuda::internal
201
202

#endif