Refactor vtkm::interop::TransferToOpenGL for implicit ArrayHandles.

Previously TransferToOpenGL would rely on every array handle implementing
the CopyInto method for transferring to work properly. This was problematic
as most Implicit arrays don't implement CopyInto.

Now we use the Devices built in Copy infrastructure to facilitate moving
data from an implicit array to concrete memory which we can be passed
to OpenGL. As an additional optimization, the temporary memory for this
interop is cached in the bufferstate.
This commit is contained in:
Robert Maynard 2017-08-15 15:56:44 -04:00
parent dfb38f9349
commit a185cd297b
7 changed files with 549 additions and 24 deletions

@ -193,9 +193,7 @@ public:
ValueType* beginPointer = this->Resource->GetMappedPoiner<ValueType>(size); ValueType* beginPointer = this->Resource->GetMappedPoiner<ValueType>(size);
//get the device pointers //get the device pointers
typedef vtkm::cont::ArrayHandle<ValueType, StorageTag> HandleType; auto portal = handle.PrepareForInput(DeviceAdapterTag());
typedef typename HandleType::template ExecutionTypes<DeviceAdapterTag>::PortalConst PortalType;
PortalType portal = handle.PrepareForInput(DeviceAdapterTag());
//Copy the data into memory that opengl owns, since we can't //Copy the data into memory that opengl owns, since we can't
//give memory from cuda to opengl //give memory from cuda to opengl

@ -21,9 +21,12 @@
#define vtk_m_interop_internal_TransferToOpenGL_h #define vtk_m_interop_internal_TransferToOpenGL_h
#include <vtkm/cont/ArrayHandle.h> #include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/StorageBasic.h> #include <vtkm/cont/StorageBasic.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/serial/DeviceAdapterSerial.h>
#include <vtkm/cont/tbb/DeviceAdapterTBB.h>
#include <vtkm/interop/BufferState.h> #include <vtkm/interop/BufferState.h>
#include <vtkm/interop/internal/OpenGLHeaders.h> #include <vtkm/interop/internal/OpenGLHeaders.h>
@ -34,6 +37,60 @@ namespace interop
namespace internal namespace internal
{ {
/// \brief smp backend and opengl interop resource management
///
/// \c TransferResource manages any extra memory allocation that is required
/// When binding an implicit array handle to opengl
///
///
class SMPTransferResource : public vtkm::interop::internal::TransferResource
{
public:
template <typename T>
SMPTransferResource(T, vtkm::Id numberOfValues)
: vtkm::interop::internal::TransferResource()
, Size(0)
, TempStorage()
{
this->resize<T>(numberOfValues);
}
~SMPTransferResource() {}
template <typename T>
void resize(vtkm::Id numberOfValues)
{
if (this->Size != numberOfValues)
{
this->Size = numberOfValues;
T* storage = new T[this->Size];
this->TempStorage.reset(reinterpret_cast<vtkm::UInt8*>(storage));
}
}
template <typename T>
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic> handle(vtkm::Id size) const
{
VTKM_ASSERT(this->Size > 0);
VTKM_ASSERT(this->Size >= size);
T* storage = reinterpret_cast<T*>(this->TempStorage.get());
//construct a handle that is a view onto the memory
return vtkm::cont::make_ArrayHandle(storage, size);
}
template <typename T>
T* as() const
{
VTKM_ASSERT(this->Size > 0);
T* storage = reinterpret_cast<T*>(this->TempStorage.get());
return storage;
}
vtkm::Id Size;
std::unique_ptr<vtkm::UInt8[]> TempStorage;
};
namespace detail namespace detail
{ {
@ -43,42 +100,47 @@ VTKM_CONT void CopyFromHandle(vtkm::cont::ArrayHandle<ValueType, StorageTag>& ha
DeviceAdapterTag) DeviceAdapterTag)
{ {
//Generic implementation that will work no matter what. We copy the data //Generic implementation that will work no matter what. We copy the data
//in the given handle to a temporary handle using the basic storage tag. //in the given handle to storage held by the buffer state.
//We then ensure the data is available in the control environment by
//synchronizing the control array. Last, we steal the array and pass the
//iterator to the rendering system
const vtkm::Id numberOfValues = handle.GetNumberOfValues(); const vtkm::Id numberOfValues = handle.GetNumberOfValues();
const GLsizeiptr size = const GLsizeiptr size =
static_cast<GLsizeiptr>(sizeof(ValueType)) * static_cast<GLsizeiptr>(numberOfValues); static_cast<GLsizeiptr>(sizeof(ValueType)) * static_cast<GLsizeiptr>(numberOfValues);
//Copy the data from its specialized Storage container to a basic heap alloc //grab the temporary storage from the buffer resource
ValueType* temporaryStorage = new ValueType[static_cast<std::size_t>(numberOfValues)]; vtkm::interop::internal::SMPTransferResource* resource =
dynamic_cast<vtkm::interop::internal::SMPTransferResource*>(state.GetResource());
#ifdef VTKM_MSVC
#pragma warning(disable : 4244)
#pragma warning(disable : 4996)
#endif
handle.CopyInto(temporaryStorage, DeviceAdapterTag());
#ifdef VTKM_MSVC
#pragma warning(default : 4996)
#pragma warning(default : 4244)
#endif
//Determine if we need to reallocate the buffer //Determine if we need to reallocate the buffer
state.SetSize(size); state.SetSize(size);
const bool resize = state.ShouldRealloc(size); const bool resize = state.ShouldRealloc(size);
if (resize) if (resize)
{ {
//Allocate the memory and set it as GL_DYNAMIC_DRAW draw //Allocate the memory and set it as GL_DYNAMIC_DRAW draw
glBufferData(state.GetType(), size, 0, GL_DYNAMIC_DRAW); glBufferData(state.GetType(), size, 0, GL_DYNAMIC_DRAW);
state.SetCapacity(size); state.SetCapacity(size);
//If we have an existing resource reallocate it to fit our new size
if (resource)
{
resource->resize<ValueType>(numberOfValues);
}
} }
//copy into opengl buffer //if we don't have a valid resource make a new one. We do this after the
glBufferSubData(state.GetType(), 0, size, temporaryStorage); //resize check so we don't double allocate when resource == nullptr and
//resize == true
if (!resource)
{
resource = new vtkm::interop::internal::SMPTransferResource(ValueType(), numberOfValues);
state.SetResource(resource);
}
delete[] temporaryStorage; using Algorithm = vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapterTag>;
auto resourceHandle = resource->handle<ValueType>(numberOfValues);
Algorithm::Copy(handle, resourceHandle);
//copy into opengl buffer
glBufferSubData(state.GetType(), 0, size, resource->as<ValueType>());
} }
template <class ValueType, class DeviceAdapterTag> template <class ValueType, class DeviceAdapterTag>

@ -20,6 +20,7 @@
set(headers set(headers
TestingOpenGLInterop.h TestingOpenGLInterop.h
TestingTransferFancyHandles.h
) )
set(unit_tests) set(unit_tests)
@ -33,11 +34,13 @@ set(needs_rendering FALSE)
if(VTKm_OSMesa_FOUND AND TARGET vtkm_rendering) if(VTKm_OSMesa_FOUND AND TARGET vtkm_rendering)
list(APPEND unit_tests list(APPEND unit_tests
UnitTestTransferOSMesa.cxx UnitTestTransferOSMesa.cxx
UnitTestFancyTransferOSMesa.cxx
) )
set(needs_rendering TRUE) set(needs_rendering TRUE)
elseif(VTKm_EGL_FOUND AND TARGET vtkm_rendering) elseif(VTKm_EGL_FOUND AND TARGET vtkm_rendering)
list(APPEND unit_tests list(APPEND unit_tests
UnitTestTransferEGL.cxx UnitTestTransferEGL.cxx
UnitTestFancyTransferEGL.cxx
) )
set(needs_rendering TRUE) set(needs_rendering TRUE)
else() else()
@ -45,6 +48,7 @@ else()
if(VTKm_GLUT_FOUND) if(VTKm_GLUT_FOUND)
list(APPEND unit_tests list(APPEND unit_tests
UnitTestTransferGLUT.cxx UnitTestTransferGLUT.cxx
UnitTestFancyTransferGLUT.cxx
) )
endif() endif()
endif() endif()

@ -0,0 +1,276 @@
//=============================================================================
//
// 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 2015 Sandia Corporation.
// Copyright 2015 UT-Battelle, LLC.
// Copyright 2015 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_cont_testing_TestingFancyArrayHandles_h
#define vtk_m_cont_testing_TestingFancyArrayHandles_h
#include <vtkm/VecTraits.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCartesianProduct.h>
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayHandleCompositeVector.h>
#include <vtkm/cont/ArrayHandleConcatenate.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/interop/TransferToOpenGL.h>
#include <vtkm/cont/testing/Testing.h>
#include <vector>
namespace vtkm
{
namespace interop
{
namespace testing
{
namespace
{
template <typename T>
vtkm::cont::ArrayHandle<T> makeArray(vtkm::Id length, T)
{
vtkm::cont::ArrayHandle<T> data;
data.Allocate(length);
auto portal = data.GetPortalControl();
for (vtkm::Id i = 0; i != data.GetNumberOfValues(); ++i)
{
portal.Set(i, TestValue(i, T()));
}
return data;
}
//bring the data back from openGL and into a std vector. Will bind the
//passed in handle to the default buffer type for the type T
template <typename T>
std::vector<T> CopyGLBuffer(GLuint& handle, T t)
{
//get the type we used for this buffer.
GLenum type = vtkm::interop::internal::BufferTypePicker(t);
//bind the buffer to the guessed buffer type, this way
//we can call CopyGLBuffer no matter what it the active buffer
glBindBuffer(type, handle);
//get the size of the buffer
int bytesInBuffer = 0;
glGetBufferParameteriv(type, GL_BUFFER_SIZE, &bytesInBuffer);
const std::size_t size = (static_cast<std::size_t>(bytesInBuffer) / sizeof(T));
//get the buffer contents and place it into a vector
std::vector<T> data;
data.resize(size);
glGetBufferSubData(type, 0, bytesInBuffer, &data[0]);
return data;
}
template <typename T, typename U>
void validate(vtkm::cont::ArrayHandle<T, U> handle, vtkm::interop::BufferState& state)
{
GLboolean is_buffer;
is_buffer = glIsBuffer(*state.GetHandle());
VTKM_TEST_ASSERT(is_buffer == GL_TRUE, "OpenGL buffer not filled");
std::vector<T> returnedValues = CopyGLBuffer(*state.GetHandle(), T());
vtkm::Int64 retSize = static_cast<vtkm::Int64>(returnedValues.size());
//since BufferState allows for re-use of a GL buffer that is slightly
//larger than the current array size, we should only check that the
//buffer is not smaller than the array.
//This GL buffer size is done to improve performance when transferring
//arrays to GL whose size changes on a per frame basis
VTKM_TEST_ASSERT(retSize >= handle.GetNumberOfValues(), "OpenGL buffer not large enough size");
//validate that retsize matches the bufferstate capacity which returns
//the amount of total GL buffer space, not the size we are using
const vtkm::Int64 capacity = (state.GetCapacity() / static_cast<vtkm::Int64>(sizeof(T)));
VTKM_TEST_ASSERT(retSize == capacity, "OpenGL buffer size doesn't match BufferState");
//validate that the capacity and the SMPTransferResource have the same size
vtkm::interop::internal::SMPTransferResource* resource =
dynamic_cast<vtkm::interop::internal::SMPTransferResource*>(state.GetResource());
VTKM_TEST_ASSERT(resource->Size == capacity,
"buffer state internal resource doesn't match BufferState capacity");
auto portal = handle.GetPortalConstControl();
auto iter = returnedValues.cbegin();
for (vtkm::Id i = 0; i != handle.GetNumberOfValues(); ++i, ++iter)
{
VTKM_TEST_ASSERT(portal.Get(i) == *iter, "incorrect value returned from OpenGL buffer");
}
}
void test_ArrayHandleCartesianProduct()
{
vtkm::cont::ArrayHandle<vtkm::Float32> x = makeArray(10, vtkm::Float32());
vtkm::cont::ArrayHandle<vtkm::Float32> y = makeArray(10, vtkm::Float32());
vtkm::cont::ArrayHandle<vtkm::Float32> z = makeArray(10, vtkm::Float32());
auto cartesian = vtkm::cont::make_ArrayHandleCartesianProduct(x, y, z);
vtkm::interop::BufferState state;
vtkm::interop::TransferToOpenGL(cartesian, state);
validate(cartesian, state);
vtkm::interop::TransferToOpenGL(cartesian, state); //make sure we can do multiple trasfers
validate(cartesian, state);
//resize up
x = makeArray(100, vtkm::Float32());
y = makeArray(100, vtkm::Float32());
z = makeArray(100, vtkm::Float32());
cartesian = vtkm::cont::make_ArrayHandleCartesianProduct(x, y, z);
vtkm::interop::TransferToOpenGL(cartesian, state);
validate(cartesian, state);
//resize down but instead capacity threshold
x = makeArray(99, vtkm::Float32());
y = makeArray(99, vtkm::Float32());
z = makeArray(99, vtkm::Float32());
cartesian = vtkm::cont::make_ArrayHandleCartesianProduct(x, y, z);
vtkm::interop::TransferToOpenGL(cartesian, state);
validate(cartesian, state);
//resize down
x = makeArray(10, vtkm::Float32());
y = makeArray(10, vtkm::Float32());
z = makeArray(10, vtkm::Float32());
cartesian = vtkm::cont::make_ArrayHandleCartesianProduct(x, y, z);
vtkm::interop::TransferToOpenGL(cartesian, state);
validate(cartesian, state);
}
void test_ArrayHandleCast()
{
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float64, 3>> handle =
makeArray(100000, vtkm::Vec<vtkm::Float64, 3>());
auto castArray = vtkm::cont::make_ArrayHandleCast(handle, vtkm::Vec<vtkm::Float32, 3>());
vtkm::interop::BufferState state;
vtkm::interop::TransferToOpenGL(castArray, state);
validate(castArray, state);
vtkm::interop::TransferToOpenGL(castArray, state); //make sure we can do multiple trasfers
validate(castArray, state);
//resize down
handle = makeArray(1000, vtkm::Vec<vtkm::Float64, 3>());
castArray = vtkm::cont::make_ArrayHandleCast(handle, vtkm::Vec<vtkm::Float32, 3>());
vtkm::interop::TransferToOpenGL(castArray, state);
validate(castArray, state);
}
void test_ArrayHandleCounting()
{
auto counting1 = vtkm::cont::make_ArrayHandleCounting(vtkm::Id(0), vtkm::Id(1), vtkm::Id(10000));
auto counting2 = vtkm::cont::make_ArrayHandleCounting(vtkm::Id(0), vtkm::Id(4), vtkm::Id(10000));
auto counting3 = vtkm::cont::make_ArrayHandleCounting(vtkm::Id(0), vtkm::Id(0), vtkm::Id(10000));
//use the same state with different counting handles
vtkm::interop::BufferState state;
vtkm::interop::TransferToOpenGL(counting1, state);
validate(counting1, state);
vtkm::interop::TransferToOpenGL(counting2, state);
validate(counting2, state);
vtkm::interop::TransferToOpenGL(counting3, state);
validate(counting3, state);
}
void test_ArrayHandleConcatenate()
{
vtkm::cont::ArrayHandle<vtkm::Float32> a = makeArray(5000, vtkm::Float32());
vtkm::cont::ArrayHandle<vtkm::Float32> b = makeArray(25000, vtkm::Float32());
auto concatenate = vtkm::cont::make_ArrayHandleConcatenate(a, b);
vtkm::interop::BufferState state;
vtkm::interop::TransferToOpenGL(concatenate, state);
validate(concatenate, state);
vtkm::interop::TransferToOpenGL(concatenate, state); //make sure we can do multiple trasfers
validate(concatenate, state);
//resize down
b = makeArray(1000, vtkm::Float32());
concatenate = vtkm::cont::make_ArrayHandleConcatenate(a, b);
vtkm::interop::TransferToOpenGL(concatenate, state);
validate(concatenate, state);
}
void test_ArrayHandleCompositeVector()
{
vtkm::cont::ArrayHandle<vtkm::Float32> x = makeArray(10000, vtkm::Float32());
vtkm::cont::ArrayHandle<vtkm::Float32> y = makeArray(10000, vtkm::Float32());
vtkm::cont::ArrayHandle<vtkm::Float32> z = makeArray(10000, vtkm::Float32());
auto composite = vtkm::cont::make_ArrayHandleCompositeVector(x, 0, y, 0, z, 0);
vtkm::interop::BufferState state;
vtkm::interop::TransferToOpenGL(composite, state);
validate(composite, state);
}
}
/// This class has a single static member, Run, that tests that all Fancy Array
/// Handles work with vtkm::interop::TransferToOpenGL
///
struct TestingTransferFancyHandles
{
public:
/// Run a suite of tests to check to see if a vtkm::interop::TransferToOpenGL
/// properly supports all the fancy array handles that vtkm supports. Returns an
/// error code that can be returned from the main function of a test.
///
struct TestAll
{
void operator()() const
{
std::cout << "Doing FancyArrayHandle TransferToOpenGL Tests" << std::endl;
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleCartesianProduct" << std::endl;
test_ArrayHandleCartesianProduct();
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleCast" << std::endl;
test_ArrayHandleCast();
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleCounting" << std::endl;
test_ArrayHandleCounting();
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleConcatenate" << std::endl;
test_ArrayHandleConcatenate();
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleConcatenate" << std::endl;
test_ArrayHandleCompositeVector();
}
};
static int Run() { return vtkm::cont::testing::Testing::Run(TestAll()); }
};
}
}
} // namespace vtkm::cont::testing
#endif //vtk_m_cont_testing_TestingFancyArrayHandles_h

@ -0,0 +1,35 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/interop/testing/TestingTransferFancyHandles.h>
#include <vtkm/rendering/CanvasEGL.h>
int UnitTestFancyTransferEGL(int, char* [])
{
//get egl canvas to construct a context for us
vtkm::rendering::CanvasEGL canvas(1024, 1024);
canvas.Initialize();
canvas.Activate();
//get glew to bind all the opengl functions
glewInit();
return vtkm::interop::testing::TestingTransferFancyHandles::Run();
}

@ -0,0 +1,115 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/interop/testing/TestingTransferFancyHandles.h>
#include <vtkm/internal/Configure.h>
#if (defined(VTKM_GCC) || defined(VTKM_CLANG))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
// OpenGL Graphics includes
//glew needs to go before glut
#include <vtkm/interop/internal/OpenGLHeaders.h>
#if defined(__APPLE__)
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#if defined(VTKM_GCC) && defined(VTKM_POSIX) && !defined(__APPLE__)
//
// 1. Some Linux distributions default linker implicitly enables the as-needed
// linking flag. This means that your shared library or executable will only
// link to libraries from which they use symbols. So if you explicitly link to
// pthread but don't use any symbols you wont have a 'DT_NEEDED' entry for
// pthread.
//
// 2. NVidia libGL (driver version 352 ) uses pthread but doesn't have
// a DT_NEEDED entry for the library. When you run ldd or readelf on the library
// you won't detect any reference to the pthread library. Aside this is odd
// since the mesa version does explicitly link to pthread. But if you run the
// following command:
// "strings /usr/lib/nvidia-352/libGL.so.1 | grep pthread | less"
// You will see the following:
// { pthread_create
// pthread_self
// pthread_equal
// pthread_key_crea
// ...
// libpthread.so.0
// libpthread.so
// pthread_create
// }
//
// This is very strong evidence that this library is using pthread.
//
//
// 3. So what does this all mean?
//
// It means that on system that use the linking flag 'as-needed', are using
// the nvidia driver, and don't use pthread will generate binaries that crash
// on launch. The only way to work around this issue is to do either:
//
//
// A: Specify 'no-as-needed' to the linker potentially causing over-linking
// and a slow down in link time.
//
// B: Use a method from pthread, making the linker realize that pthread is
// needed. Note we have to actually call the method so that a linker with
// optimizations enabled doesn't remove the function and pthread requirement.
//
//
// So that is the explanation on why we have the following function which is
// used once, doesn't look to be useful and seems very crazy.
#include <iostream>
#include <pthread.h>
#define VTKM_NVIDIA_PTHREAD_WORKAROUND 1
static int vtkm_force_linking_to_pthread_to_fix_nvidia_libgl_bug()
{
return static_cast<int>(pthread_self());
}
#endif
int UnitTestFancyTransferGLUT(int argc, char* argv[])
{
//get glut to construct a context for us
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(1024, 1024);
glutCreateWindow("GLUT test");
//get glew to bind all the opengl functions
glewInit();
if (!glewIsSupported("GL_VERSION_2_1"))
{
std::cerr << glGetString(GL_RENDERER) << std::endl;
std::cerr << glGetString(GL_VERSION) << std::endl;
return 1;
}
#if defined(VTKM_NVIDIA_PTHREAD_WORKAROUND)
std::cout << ::vtkm_force_linking_to_pthread_to_fix_nvidia_libgl_bug();
#endif
return vtkm::interop::testing::TestingTransferFancyHandles::Run();
}

@ -0,0 +1,35 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/interop/testing/TestingTransferFancyHandles.h>
#include <vtkm/rendering/CanvasOSMesa.h>
int UnitTestFancyTransferOSMesa(int, char* [])
{
//get osmesa canvas to construct a context for us
vtkm::rendering::CanvasOSMesa canvas(1024, 1024);
canvas.Initialize();
canvas.Activate();
//get glew to bind all the opengl functions
glewInit();
return vtkm::interop::testing::TestingTransferFancyHandles::Run();
}