Commit a185cd29 authored by Robert Maynard's avatar Robert Maynard

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.
parent dfb38f93
......@@ -193,9 +193,7 @@ public:
ValueType* beginPointer = this->Resource->GetMappedPoiner<ValueType>(size);
//get the device pointers
typedef vtkm::cont::ArrayHandle<ValueType, StorageTag> HandleType;
typedef typename HandleType::template ExecutionTypes<DeviceAdapterTag>::PortalConst PortalType;
PortalType portal = handle.PrepareForInput(DeviceAdapterTag());
auto portal = handle.PrepareForInput(DeviceAdapterTag());
//Copy the data into memory that opengl owns, since we can't
//give memory from cuda to opengl
......
......@@ -21,9 +21,12 @@
#define vtk_m_interop_internal_TransferToOpenGL_h
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.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/internal/OpenGLHeaders.h>
......@@ -34,6 +37,60 @@ namespace interop
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
{
......@@ -43,42 +100,47 @@ VTKM_CONT void CopyFromHandle(vtkm::cont::ArrayHandle<ValueType, StorageTag>& ha
DeviceAdapterTag)
{
//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.
//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
//in the given handle to storage held by the buffer state.
const vtkm::Id numberOfValues = handle.GetNumberOfValues();
const GLsizeiptr size =
static_cast<GLsizeiptr>(sizeof(ValueType)) * static_cast<GLsizeiptr>(numberOfValues);
//Copy the data from its specialized Storage container to a basic heap alloc
ValueType* temporaryStorage = new ValueType[static_cast<std::size_t>(numberOfValues)];
#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
//grab the temporary storage from the buffer resource
vtkm::interop::internal::SMPTransferResource* resource =
dynamic_cast<vtkm::interop::internal::SMPTransferResource*>(state.GetResource());
//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);
//If we have an existing resource reallocate it to fit our new size
if (resource)
{
resource->resize<ValueType>(numberOfValues);
}
}
//copy into opengl buffer
glBufferSubData(state.GetType(), 0, size, temporaryStorage);
//if we don't have a valid resource make a new one. We do this after the
//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>
......
......@@ -20,6 +20,7 @@
set(headers
TestingOpenGLInterop.h
TestingTransferFancyHandles.h
)
set(unit_tests)
......@@ -33,11 +34,13 @@ set(needs_rendering FALSE)
if(VTKm_OSMesa_FOUND AND TARGET vtkm_rendering)
list(APPEND unit_tests
UnitTestTransferOSMesa.cxx
UnitTestFancyTransferOSMesa.cxx
)
set(needs_rendering TRUE)
elseif(VTKm_EGL_FOUND AND TARGET vtkm_rendering)
list(APPEND unit_tests
UnitTestTransferEGL.cxx
UnitTestFancyTransferEGL.cxx
)
set(needs_rendering TRUE)
else()
......@@ -45,6 +48,7 @@ else()
if(VTKm_GLUT_FOUND)
list(APPEND unit_tests
UnitTestTransferGLUT.cxx
UnitTestFancyTransferGLUT.cxx
)
endif()
endif()
......
This diff is collapsed.
//============================================================================
// 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();
}
//============================================================================
// 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();
}
//============================================================================
// 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();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment