Merge topic 'more_efficient_opengl_interop'

4d270187 Update the opengl interop code to be significantly faster with cuda.

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !261
This commit is contained in:
Robert Maynard 2015-11-11 09:40:07 -05:00 committed by Kitware Robot
commit 88a605549e
8 changed files with 393 additions and 123 deletions

@ -56,9 +56,10 @@ struct HelloVTKMInterop
vtkm::Vec< vtkm::Int32, 2 > Dims;
GLuint ProgramId;
GLuint VBOId;
GLuint VAOId;
GLuint ColorId;
vtkm::opengl::BufferState VBOState;
vtkm::opengl::BufferState ColorState;
vtkm::cont::Timer<DeviceAdapter> Timer;
@ -70,9 +71,9 @@ struct HelloVTKMInterop
HelloVTKMInterop(vtkm::Int32 width, vtkm::Int32 height):
Dims(256,256),
ProgramId(),
VBOId(),
VAOId(),
ColorId(),
VBOState(),
ColorState(),
Timer(),
InputData(),
InHandle(),
@ -119,11 +120,11 @@ struct HelloVTKMInterop
glUniformMatrix4fv( unifLoc, 1, GL_FALSE, mvp );
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, this->VBOId);
glBindBuffer(GL_ARRAY_BUFFER, *this->VBOState.GetHandle());
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, 0 );
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, this->ColorId);
glBindBuffer(GL_ARRAY_BUFFER, *this->ColorState.GetHandle());
glColorPointer(4, GL_UNSIGNED_BYTE, 0, 0 );
glDrawArrays( GL_POINTS, 0, arraySize );
@ -165,8 +166,8 @@ struct HelloVTKMInterop
GenerateSurfaceWorklet worklet( t );
DispatcherType(worklet).Invoke( this->InHandle, this->OutCoords, this->OutColors );
vtkm::opengl::TransferToOpenGL( this->OutCoords, this->VBOId, DeviceAdapter() );
vtkm::opengl::TransferToOpenGL( this->OutColors, this->ColorId, DeviceAdapter() );
vtkm::opengl::TransferToOpenGL( this->OutCoords, this->VBOState, DeviceAdapter() );
vtkm::opengl::TransferToOpenGL( this->OutColors, this->ColorState, DeviceAdapter() );
this->render();
if(t > 10)

188
vtkm/opengl/BufferState.h Normal file

@ -0,0 +1,188 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
// 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.
//
// Copyright 2014 Sandia Corporation.
// Copyright 2014 UT-Battelle, LLC.
// Copyright 2014 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_opengl_BufferState_h
#define vtk_m_opengl_BufferState_h
//gl headers needs to be buffer anything to do with buffer's
#include <vtkm/opengl/internal/OpenGLHeaders.h>
#include <vtkm/opengl/internal/BufferTypePicker.h>
VTKM_THIRDPARTY_PRE_INCLUDE
#include <boost/smart_ptr/scoped_ptr.hpp>
VTKM_THIRDPARTY_POST_INCLUDE
namespace vtkm{
namespace opengl{
namespace internal
{
/// \brief Device backend and opengl interop resources management
///
/// \c TransferResource manages a context for a given device backend and a
/// single OpenGL buffer as efficiently as possible.
///
/// Default implementation is a no-op
class TransferResource
{
public:
virtual ~TransferResource() {}
};
}
/// \brief Manages the state for transferring an ArrayHandle to opengl.
///
/// \c BufferState holds all the relevant data information for a given ArrayHandle
/// mapping into OpenGL. Reusing the state information for all renders of an
/// ArrayHandle will allow for the most efficient interop between backends and
/// OpenGL ( especially for CUDA ).
///
///
/// The interop code in vtk-m uses a lazy buffer re-allocation.
///
class BufferState
{
public:
/// Construct a BufferState using an existing GLHandle
BufferState(GLuint& gLHandle):
OpenGLHandle(&gLHandle),
BufferType(GL_INVALID_VALUE),
SizeOfActiveSection(0),
CapacityOfBuffer(0),
DefaultGLHandle(0),
Resource(NULL)
{
}
/// Construct a BufferState using an existing GLHandle and type
BufferState(GLuint& gLHandle, GLenum type):
OpenGLHandle(&gLHandle),
BufferType(type),
SizeOfActiveSection(0),
CapacityOfBuffer(0),
DefaultGLHandle(0),
Resource(NULL)
{
}
BufferState():
OpenGLHandle(NULL),
BufferType(GL_INVALID_VALUE),
SizeOfActiveSection(0),
CapacityOfBuffer(0),
DefaultGLHandle(0),
Resource(NULL)
{
this->OpenGLHandle = &this->DefaultGLHandle;
}
~BufferState()
{
//don't delete this as it points to user memory, or stack allocated
//memory inside this object instance
this->OpenGLHandle = NULL;
}
/// \brief get the OpenGL buffer handle
///
GLuint* GetHandle() const { return this->OpenGLHandle; }
/// \brief return if this buffer has a valid OpenGL buffer type
///
bool HasType() const { return this->BufferType != GL_INVALID_VALUE; }
/// \brief return what OpenGL buffer type we are bound to
///
/// will return GL_INVALID_VALUE if we don't have a valid type set
GLenum GetType() const { return this->BufferType; }
/// \brief Set what type of OpenGL buffer type we should bind as
///
void SetType(GLenum type) { this->BufferType = type; }
/// \brief deduce the buffer type from the template value type that
/// was passed in, and set that as our type
///
/// Will be GL_ELEMENT_ARRAY_BUFFER for
/// vtkm::Int32, vtkm::UInt32, vtkm::Int64, vtkm::UInt64, vtkm::Id, and vtkm::IdComponent
/// will be GL_ARRAY_BUFFER for everything else.
template<typename T>
void DeduceAndSetType(T t)
{ this->BufferType = vtkm::opengl::internal::BufferTypePicker(t); }
/// \brief Get the size of the buffer in bytes
///
/// Get the size of the active section of the buffer
///This will always be <= the capacity of the buffer
vtkm::Int64 GetSize() const { return this->SizeOfActiveSection; }
//Set the size of buffer in bytes
//This will always needs to be <= the capacity of the buffer
//Note: This call should only be used internally by vtk-m
void SetSize(vtkm::Int64 size) { this->SizeOfActiveSection = size; }
/// \brief Get the capacity of the buffer in bytes
///
/// The buffers that vtk-m allocate in OpenGL use lazy resizing. This allows
/// vtk-m to not have to reallocate a buffer while the size stays the same
/// or shrinks. This allows allows the cuda to OpenGL to perform significantly
/// better as we than don't need to call cudaGraphicsGLRegisterBuffer as
/// often
vtkm::Int64 GetCapacity() const { return this->CapacityOfBuffer; }
// Helper function to compute when we should resize the capacity of the
// buffer
bool ShouldRealloc(vtkm::Int64 desiredSize) const
{
const bool haveNotEnoughRoom = this->GetCapacity() < desiredSize;
const bool haveTooMuchRoom = this->GetCapacity() > (desiredSize*2);
return (haveNotEnoughRoom || haveTooMuchRoom);
}
//Set the capacity of buffer in bytes
//The capacity of a buffer can be larger than the active size of buffer
//Note: This call should only be used internally by vtk-m
void SetCapacity(vtkm::Int64 capacity) { this->CapacityOfBuffer = capacity; }
//Note: This call should only be used internally by vtk-m
vtkm::opengl::internal::TransferResource* GetResource()
{ return this->Resource.get(); }
//Note: This call should only be used internally by vtk-m
void SetResource( vtkm::opengl::internal::TransferResource* resource)
{ this->Resource.reset(resource); }
private:
//explicitly state the BufferState doesn't support copy or move semantics
BufferState(const BufferState&);
void operator=(const BufferState&);
GLuint* OpenGLHandle;
GLenum BufferType;
vtkm::Int64 SizeOfActiveSection; //must be Int64 as size can be over 2billion
vtkm::Int64 CapacityOfBuffer; //must be Int64 as size can be over 2billion
GLuint DefaultGLHandle;
boost::scoped_ptr<vtkm::opengl::internal::TransferResource> Resource;
};
}}
#endif //vtk_m_opengl_BufferState_h

@ -19,6 +19,7 @@
##============================================================================
set(headers
BufferState.h
TransferToOpenGL.h
)

@ -21,37 +21,21 @@
#define vtk_m_opengl_TransferToOpenGL_h
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/opengl/BufferState.h>
#include <vtkm/opengl/internal/TransferToOpenGL.h>
namespace vtkm{
namespace opengl{
/// \brief Manages transferring an ArrayHandle to opengl .
///
/// \c TransferToOpenGL manages to transfer the contents of an ArrayHandle
/// to OpenGL as efficiently as possible. Will return the type of array buffer
/// that we have bound the handle too. Will be GL_ELEMENT_ARRAY_BUFFER for
/// vtkm::Id, and GL_ARRAY_BUFFER for everything else.
///
/// This function keeps the buffer as the active buffer of the returned type.
///
/// This function will throw exceptions if the transfer wasn't possible
///
template<typename ValueType, class StorageTag, class DeviceAdapterTag>
VTKM_CONT_EXPORT
GLenum TransferToOpenGL(vtkm::cont::ArrayHandle<ValueType,StorageTag> handle,
GLuint& openGLHandle,
DeviceAdapterTag)
{
vtkm::opengl::internal::TransferToOpenGL<ValueType, DeviceAdapterTag> toGL;
toGL.Transfer(handle,openGLHandle);
return toGL.GetType();
}
/// \brief Manages transferring an ArrayHandle to opengl .
///
/// \c TransferToOpenGL manages to transfer the contents of an ArrayHandle
/// to OpenGL as efficiently as possible. Will use the given \p type as how
/// to bind the buffer.
/// to OpenGL as efficiently as possible. Will use the given \p state to determine
/// what buffer handle to use, and the type to bind the buffer handle too.
/// If the type of buffer hasn't been determined the transfer will use
/// deduceAndSetBufferType to do so. Lastly state also holds on to per backend resources
/// that allow for efficient updating to open gl
///
/// This function keeps the buffer as the active buffer of the input type.
///
@ -60,12 +44,11 @@ GLenum TransferToOpenGL(vtkm::cont::ArrayHandle<ValueType,StorageTag> handle,
template<typename ValueType, class StorageTag, class DeviceAdapterTag>
VTKM_CONT_EXPORT
void TransferToOpenGL(vtkm::cont::ArrayHandle<ValueType, StorageTag> handle,
GLuint& openGLHandle,
GLenum type,
BufferState& state,
DeviceAdapterTag)
{
vtkm::opengl::internal::TransferToOpenGL<ValueType, DeviceAdapterTag> toGL(type);
toGL.Transfer(handle,openGLHandle);
vtkm::opengl::internal::TransferToOpenGL<ValueType, DeviceAdapterTag> toGL(state);
return toGL.Transfer(handle);
}
}}

@ -39,6 +39,94 @@ namespace vtkm {
namespace opengl {
namespace internal {
/// \brief cuda backend and opengl interop resource management
///
/// \c TransferResource manages cuda resource binding for a given buffer
///
///
class CudaTransferResource : public vtkm::opengl::internal::TransferResource
{
public:
CudaTransferResource():
vtkm::opengl::internal::TransferResource()
{
this->Registered = false;
}
~CudaTransferResource()
{
//unregister the buffer
if(this->Registered)
{
cudaGraphicsUnregisterResource(this->CudaResource);
}
}
bool IsRegistered() const { return Registered; }
void Register(GLuint* handle)
{
if(this->Registered)
{
//If you don't release the cuda resource before re-registering you
//will leak memory on the OpenGL side.
cudaGraphicsUnregisterResource(this->CudaResource);
}
this->Registered = true;
cudaError_t cError = cudaGraphicsGLRegisterBuffer(&this->CudaResource,
*handle,
cudaGraphicsMapFlagsWriteDiscard);
if(cError != cudaSuccess)
{
throw vtkm::cont::ErrorExecution(
"Could not register the OpenGL buffer handle to CUDA.");
}
}
void Map()
{
//map the resource into cuda, so we can copy it
cudaError_t cError =cudaGraphicsMapResources(1,&this->CudaResource);
if(cError != cudaSuccess)
{
throw vtkm::cont::ErrorControlBadAllocation(
"Could not allocate enough memory in CUDA for OpenGL interop.");
}
}
template< typename ValueType >
ValueType* GetMappedPoiner( vtkm::Int64 desiredSize)
{
//get the mapped pointer
std::size_t cuda_size;
ValueType* pointer = NULL;
cudaError_t cError = cudaGraphicsResourceGetMappedPointer((void **)&pointer,
&cuda_size,
this->CudaResource);
if(cError != cudaSuccess)
{
throw vtkm::cont::ErrorExecution(
"Unable to get pointers to CUDA memory for OpenGL buffer.");
}
//assert that cuda_size is the same size as the buffer we created in OpenGL
VTKM_ASSERT_CONT(cuda_size >= desiredSize);
return pointer;
}
void UnMap()
{
cudaGraphicsUnmapResources(1, &this->CudaResource);
}
private:
bool Registered;
cudaGraphicsResource_t CudaResource;
};
/// \brief Manages transferring an ArrayHandle to opengl .
///
/// \c TransferToOpenGL manages to transfer the contents of an ArrayHandle
@ -49,72 +137,65 @@ class TransferToOpenGL<ValueType, vtkm::cont::DeviceAdapterTagCuda>
{
typedef vtkm::cont::DeviceAdapterTagCuda DeviceAdapterTag;
public:
VTKM_CONT_EXPORT TransferToOpenGL():
Type( vtkm::opengl::internal::BufferTypePicker( ValueType() ) )
{}
VTKM_CONT_EXPORT explicit TransferToOpenGL(BufferState& state):
State(state),
Resource(NULL)
{
if( !this->State.HasType() )
{
this->State.DeduceAndSetType( ValueType() );
}
VTKM_CONT_EXPORT explicit TransferToOpenGL(GLenum type):
Type(type)
{}
this->Resource = dynamic_cast<vtkm::opengl::internal::CudaTransferResource*>
(state.GetResource());
if( !this->Resource )
{
vtkm::opengl::internal::CudaTransferResource* cudaResource =
new vtkm::opengl::internal::CudaTransferResource();
GLenum GetType() const { return this->Type; }
//reset the resource to be a cuda resource
this->State.SetResource( cudaResource );
this->Resource = cudaResource;
}
}
template< typename StorageTag >
VTKM_CONT_EXPORT
void Transfer (
vtkm::cont::ArrayHandle<ValueType, StorageTag> &handle,
GLuint& openGLHandle ) const
vtkm::cont::ArrayHandle<ValueType, StorageTag> &handle) const
{
//construct a cuda resource handle
cudaGraphicsResource_t cudaResource;
cudaError_t cError;
//make a buffer for the handle if the user has forgotten too
if(!glIsBuffer(openGLHandle))
{
glGenBuffers(1,&openGLHandle);
}
if(!glIsBuffer(*this->State.GetHandle()))
{
glGenBuffers(1, this->State.GetHandle());
}
//bind the buffer to the given buffer type
glBindBuffer(this->Type, openGLHandle);
glBindBuffer(this->State.GetType(), *this->State.GetHandle());
//Allocate the memory and set it as GL_DYNAMIC_DRAW draw
const vtkm::Id size = static_cast<vtkm::Id>(sizeof(ValueType))* handle.GetNumberOfValues();
glBufferData(this->Type, size, 0, GL_DYNAMIC_DRAW);
//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(), size, 0, GL_DYNAMIC_DRAW);
//register the buffer as being used by cuda
cError = cudaGraphicsGLRegisterBuffer(&cudaResource,
openGLHandle,
cudaGraphicsMapFlagsWriteDiscard);
if(cError != cudaSuccess)
{
throw vtkm::cont::ErrorExecution(
"Could not register the OpenGL buffer handle to CUDA.");
}
this->State.SetCapacity(size);
}
//map the resource into cuda, so we can copy it
cError =cudaGraphicsMapResources(1,&cudaResource);
if(cError != cudaSuccess)
{
throw vtkm::cont::ErrorControlBadAllocation(
"Could not allocate enough memory in CUDA for OpenGL interop.");
}
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());
}
//get the mapped pointer
std::size_t cuda_size;
ValueType* beginPointer=NULL;
cError = cudaGraphicsResourceGetMappedPointer((void **)&beginPointer,
&cuda_size,
cudaResource);
this->Resource->Map();
if(cError != cudaSuccess)
{
throw vtkm::cont::ErrorExecution(
"Unable to get pointers to CUDA memory for OpenGL buffer.");
}
//assert that cuda_size is the same size as the buffer we created in OpenGL
VTKM_ASSERT_CONT(cuda_size == size);
ValueType* beginPointer= this->Resource->GetMappedPoiner<ValueType>(size);
//get the device pointers
typedef vtkm::cont::ArrayHandle<ValueType, StorageTag> HandleType;
@ -134,14 +215,12 @@ public:
thrust::cuda::pointer<ValueType>(beginPointer));
//unmap the resource
cudaGraphicsUnmapResources(1, &cudaResource);
//unregister the buffer
cudaGraphicsUnregisterResource(cudaResource);
this->Resource->UnMap();
}
private:
GLenum Type;
vtkm::opengl::BufferState& State;
vtkm::opengl::internal::CudaTransferResource* Resource;
};

@ -30,10 +30,10 @@ namespace internal {
/// helper function that guesses what OpenGL buffer type is the best default
/// given a primitive type. Currently GL_ELEMENT_ARRAY_BUFFER is used for integer
/// types, and GL_ARRAY_BUFFER is used for everything else
VTKM_CONT_EXPORT GLenum BufferTypePicker( int )
VTKM_CONT_EXPORT GLenum BufferTypePicker( vtkm::Int32 )
{ return GL_ELEMENT_ARRAY_BUFFER; }
VTKM_CONT_EXPORT GLenum BufferTypePicker( unsigned int )
VTKM_CONT_EXPORT GLenum BufferTypePicker( vtkm::UInt32 )
{ return GL_ELEMENT_ARRAY_BUFFER; }
#if VTKM_SIZE_LONG == 8

@ -25,7 +25,9 @@
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/opengl/internal/OpenGLHeaders.h>
#include <vtkm/opengl/internal/BufferTypePicker.h>
#include <vtkm/opengl/BufferState.h>
namespace vtkm {
namespace opengl {
namespace internal {
@ -37,7 +39,7 @@ template<class ValueType, class StorageTag, class DeviceAdapterTag>
VTKM_CONT_EXPORT
void CopyFromHandle(
vtkm::cont::ArrayHandle<ValueType, StorageTag>& handle,
GLenum type,
vtkm::opengl::BufferState& state,
DeviceAdapterTag)
{
//Generic implementation that will work no matter what. We copy the data
@ -61,11 +63,19 @@ void CopyFromHandle(
//Note that the temporary ArrayHandle is no longer valid after this call
ValueType* temporaryStorage = tmpHandle.Internals->ControlArray.StealArray();
//Detach the current buffer
glBufferData(type, size, 0, GL_DYNAMIC_DRAW);
//Determine if we need to reallocate the buffer
state.SetSize(size);
const bool resize = state.ShouldRealloc(size);
if( resize )
{
//Allocate the memory and set it as GL_DYNAMIC_DRAW draw
glBufferData(state.GetType(), size, 0, GL_DYNAMIC_DRAW);
//Allocate the memory and set it as static draw and copy into opengl
glBufferSubData(type,0,size,temporaryStorage);
state.SetCapacity(size);
}
//copy into opengl buffer
glBufferSubData(state.GetType(),0,size,temporaryStorage);
delete[] temporaryStorage;
}
@ -74,7 +84,7 @@ template<class ValueType, class DeviceAdapterTag>
VTKM_CONT_EXPORT
void CopyFromHandle(
vtkm::cont::ArrayHandle<ValueType, vtkm::cont::StorageTagBasic>& handle,
GLenum type,
vtkm::opengl::BufferState& state,
DeviceAdapterTag)
{
//Specialization given that we are use an C allocated array storage tag
@ -87,13 +97,21 @@ void CopyFromHandle(
static_cast<GLsizeiptr>(sizeof(ValueType)) *
static_cast<GLsizeiptr>(handle.GetNumberOfValues());
//Detach the current buffer
glBufferData(type, size, 0, GL_DYNAMIC_DRAW);
//Determine if we need to reallocate the buffer
state.SetSize(size);
const bool resize = state.ShouldRealloc(size);
if( resize )
{
//Allocate the memory and set it as GL_DYNAMIC_DRAW draw
glBufferData(state.GetType(), size, 0, GL_DYNAMIC_DRAW);
state.SetCapacity(size);
}
//Allocate the memory and set it as static draw and copy into opengl
const ValueType* memory = &(*vtkm::cont::ArrayPortalToIteratorBegin(
handle.PrepareForInput(DeviceAdapterTag())));
glBufferSubData(type,0,size,memory);
glBufferSubData(state.GetType(),0,size,memory);
}
@ -108,30 +126,28 @@ template<typename ValueType, class DeviceAdapterTag>
class TransferToOpenGL
{
public:
VTKM_CONT_EXPORT TransferToOpenGL():
Type( vtkm::opengl::internal::BufferTypePicker( ValueType() ) )
{}
VTKM_CONT_EXPORT explicit TransferToOpenGL(GLenum type):
Type(type)
{}
VTKM_CONT_EXPORT GLenum GetType() const { return this->Type; }
VTKM_CONT_EXPORT explicit TransferToOpenGL(BufferState& state):
State(state)
{
if( !this->State.HasType() )
{
this->State.DeduceAndSetType( ValueType() );
}
}
template< typename StorageTag >
VTKM_CONT_EXPORT
void Transfer (
vtkm::cont::ArrayHandle<ValueType, StorageTag>& handle,
GLuint& openGLHandle ) const
vtkm::cont::ArrayHandle<ValueType, StorageTag>& handle) const
{
//make a buffer for the handle if the user has forgotten too
if(!glIsBuffer(openGLHandle))
{
glGenBuffers(1,&openGLHandle);
}
if(!glIsBuffer(*this->State.GetHandle()))
{
glGenBuffers(1, this->State.GetHandle());
}
//bind the buffer to the given buffer type
glBindBuffer(this->Type, openGLHandle);
glBindBuffer(this->State.GetType(), *this->State.GetHandle());
//transfer the data.
//the primary concern that we have at this point is data locality and
@ -147,10 +163,10 @@ public:
//
//The end result is that we have CopyFromHandle which does number two
//from StorageTagBasic, and does the CopyInto for everything else
detail::CopyFromHandle(handle, this->Type, DeviceAdapterTag());
detail::CopyFromHandle(handle, this->State, DeviceAdapterTag());
}
private:
GLenum Type;
vtkm::opengl::BufferState& State;
};
}

@ -75,7 +75,8 @@ private:
{
try
{
vtkm::opengl::TransferToOpenGL(array,handle, DeviceAdapterTag());
vtkm::opengl::BufferState state(handle);
vtkm::opengl::TransferToOpenGL(array, state, DeviceAdapterTag());
}
catch (vtkm::cont::ErrorControlBadAllocation error)
{
@ -97,7 +98,8 @@ private:
{
try
{
vtkm::opengl::TransferToOpenGL(array,handle,type, DeviceAdapterTag());
vtkm::opengl::BufferState state(handle, type);
vtkm::opengl::TransferToOpenGL(array, state, DeviceAdapterTag());
}
catch (vtkm::cont::ErrorControlBadAllocation error)
{