gpuCache/gpuCacheSample.cpp

gpuCache/gpuCacheSample.cpp
//-
//**************************************************************************/
// Copyright 2015 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
//**************************************************************************/
//+
#include "gpuCacheSample.h"
#include "gpuCacheVBOProxy.h"
#include <Alembic/Util/Murmur3.h>
#include <boost/weak_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <boost/foreach.hpp>
#include <cassert>
namespace {
using namespace GPUCache;
//==============================================================================
// LOCAL FUNCTIONS & CLASSES
//==============================================================================
//==============================================================================
// CLASS ArrayBaseImp
//==============================================================================
class ArrayBaseImp
{
public:
typedef ArrayBase::Callback Callback;
typedef ArrayBase::Key Key;
static void registerCreationCallback(Callback callback)
{
creationCallbacks.push_back(callback);
}
static void unregisterCreationCallback(Callback callback)
{
Callbacks::iterator it = std::find(
creationCallbacks.begin(), creationCallbacks.end(), callback);
if (it != creationCallbacks.end()) {
creationCallbacks.erase(it);
}
}
static void invokeCreationCallback(const Key& key)
{
BOOST_FOREACH(const Callback& callback, creationCallbacks) {
(*callback)(key);
}
}
static void registerDestructionCallback(Callback callback)
{
destructionCallbacks.push_back(callback);
}
static void unregisterDestructionCallback(Callback callback)
{
Callbacks::iterator it = std::find(
destructionCallbacks.begin(), destructionCallbacks.end(), callback);
if (it != destructionCallbacks.end()) {
destructionCallbacks.erase(it);
}
}
static void invokeDestructionCallback(const Key& key)
{
BOOST_FOREACH(const Callback& callback, destructionCallbacks) {
(*callback)(key);
}
}
private:
typedef std::vector<ArrayBase::Callback> Callbacks;
static Callbacks creationCallbacks;
static Callbacks destructionCallbacks;
};
ArrayBaseImp::Callbacks ArrayBaseImp::creationCallbacks;
ArrayBaseImp::Callbacks ArrayBaseImp::destructionCallbacks;
//==============================================================================
// CLASS ArrayRegistryImp
//==============================================================================
template <typename T>
class ArrayRegistryImp
{
public:
typedef ArrayBase::Digest Digest;
typedef ArrayBase::Key Key;
typedef ArrayBase::KeyHash KeyHash;
typedef ArrayBase::KeyEqualTo KeyEqualTo;
static ArrayRegistryImp<T>& singleton()
{ return fsSingleton; }
~ArrayRegistryImp()
{
// Unfortunately, we can't check that all buffers have been
// freed here. The reason is Maya does not take the time to
// clean-up the dependency graph when exiting. Therefore,
// there might still exist some ShapeNode alived at exit time
// and these will indirectly keep these buffers alive.
//
// The way to check that the mechanism is working correctly is
// therefore to perform the following MEL commands "file -f
// -new; gpuCache -q -sgs;" and check that everything has
// been freed. The gpuCache regression test does that.
//
// assert(fMap.size() == 0);
}
tbb::mutex& mutex()
{ return fMutex; }
boost::shared_ptr<Array<T> > lookup(
const Digest& digest,
size_t size
)
{
// Caller will accept either readable or non-readable. First look for non-readable.
boost::shared_ptr<Array<T> > ret = lookupNonReadable(digest, size);
if (!ret) {
ret = lookupReadable(digest, size);
}
return ret;
}
boost::shared_ptr<Array<T> > lookupNonReadable(
const Digest& digest,
size_t size
)
{
typename Map::const_iterator it = fMapNonReadable.find(Key(size * sizeof(T), digest));
if (it != fMapNonReadable.end()) {
// Might return null if the weak_ptr<> is now dangling
// but not yet removed from the map...
boost::shared_ptr<Array<T> > ret = it->second.lock();
if (!ret) {
fMapNonReadable.erase(it);
}
return ret;
}
else {
return boost::shared_ptr<Array<T> >();
}
}
boost::shared_ptr<ReadableArray<T> > lookupReadable(
const Digest& digest,
size_t size
)
{
typename MapReadable::const_iterator it = fMapReadable.find(Key(size * sizeof(T), digest));
if (it != fMapReadable.end()) {
// Might return null if the weak_ptr<> is now dangling
// but not yet removed from the map...
boost::shared_ptr<ReadableArray<T> > ret = it->second.lock();
if (!ret) {
fMapReadable.erase(it);
}
return ret;
}
else {
return boost::shared_ptr<ReadableArray<T> >();
}
}
void insert(boost::shared_ptr<Array<T> > array)
{
if (array->isReadable()) {
fMapReadable.insert(std::make_pair(array->key(), array->getReadableArray()));
} else {
fMapNonReadable.insert(std::make_pair(array->key(), array));
}
}
void removeIfStaled(const Key& key, bool readable)
{
if (readable) {
typename MapReadable::const_iterator it = fMapReadable.find(key);
if (it != fMapReadable.end()) {
// Might return null if the weak_ptr<> is now dangling
// but not yet removed from the map...
boost::shared_ptr<Array<T> > ret = it->second.lock();
if (!ret) {
// Get rid of the stalled entry so that insert() can
// work properly.
fMapReadable.erase(it);
}
}
} else {
typename Map::const_iterator it = fMapNonReadable.find(key);
if (it != fMapNonReadable.end()) {
// Might return null if the weak_ptr<> is now dangling
// but not yet removed from the map...
boost::shared_ptr<Array<T> > ret = it->second.lock();
if (!ret) {
// Get rid of the stalled entry so that insert() can
// work properly.
fMapNonReadable.erase(it);
}
}
}
}
private:
typedef boost::unordered_map<
Key,
boost::weak_ptr<Array<T> >,
KeyHash,
KeyEqualTo> Map;
typedef boost::unordered_map<
Key,
boost::weak_ptr<ReadableArray<T> >,
KeyHash,
KeyEqualTo> MapReadable;
static ArrayRegistryImp fsSingleton;
tbb::mutex fMutex;
Map fMapNonReadable;
MapReadable fMapReadable;
};
template <typename T>
ArrayRegistryImp<T> ArrayRegistryImp<T>::fsSingleton;
template class ArrayRegistryImp<IndexBuffer::index_t>;
template class ArrayRegistryImp<float>;
//==============================================================================
// CLASS IndexBufferRegistry
//==============================================================================
class IndexBufferRegistry
{
public:
typedef IndexBuffer::index_t index_t;
typedef IndexBuffer::Key Key;
typedef IndexBuffer::KeyHash KeyHash;
typedef IndexBuffer::KeyEqualTo KeyEqualTo;
static IndexBufferRegistry& singleton()
{ return fsSingleton; }
~IndexBufferRegistry() {}
tbb::mutex& mutex()
{ return fMutex; }
boost::shared_ptr<IndexBuffer> lookup(
const boost::shared_ptr<Array<index_t> >& array,
const size_t beginIdx,
const size_t endIdx
)
{
Map::const_iterator it = fMap.find(Key(array, beginIdx, endIdx));
if (it != fMap.end()) {
// Might return null if the weak_ptr<> is now dangling
// but not yet removed from the map...
boost::shared_ptr<IndexBuffer> ret = it->second.lock();
if (!ret) {
// Get rid of the stalled entry so that insert() can
// work properly.
fMap.erase(it);
}
return ret;
}
else {
return boost::shared_ptr<IndexBuffer>();
}
}
void insert(boost::shared_ptr<IndexBuffer> buffer)
{
fMap.insert(
std::make_pair(
Key(buffer->array(), buffer->beginIdx(), buffer->endIdx()),
buffer));
}
void removeIfStaled(
const boost::shared_ptr<Array<index_t> >& array,
const size_t beginIdx,
const size_t endIdx
)
{
Map::const_iterator it = fMap.find(Key(array, beginIdx, endIdx));
if (it != fMap.end()) {
// Might return null if the weak_ptr<> is now dangling
// but not yet removed from the map...
boost::shared_ptr<IndexBuffer> ret = it->second.lock();
if (!ret) {
// Get rid of the stalled entry so that insert() can
// work properly.
fMap.erase(it);
}
}
}
size_t nbAllocated()
{ return fMap.size(); }
size_t nbAllocatedBytes()
{
size_t bytes = 0;
BOOST_FOREACH(const Map::value_type& v, fMap) {
boost::shared_ptr<IndexBuffer> buf = v.second.lock();
if (buf) {
bytes += buf->bytes();
}
}
return bytes;
}
private:
typedef boost::unordered_map<
Key,
boost::weak_ptr<IndexBuffer>,
KeyHash,
KeyEqualTo
> Map;
static IndexBufferRegistry fsSingleton;
tbb::mutex fMutex;
Map fMap;
};
IndexBufferRegistry IndexBufferRegistry::fsSingleton;
//==============================================================================
// CLASS VertexBufferRegistry
//==============================================================================
class VertexBufferRegistry
{
public:
typedef VertexBuffer::Key Key;
typedef VertexBuffer::KeyHash KeyHash;
typedef VertexBuffer::KeyEqualTo KeyEqualTo;
static VertexBufferRegistry& singleton()
{ return fsSingleton; }
~VertexBufferRegistry() {}
tbb::mutex& mutex()
{ return fMutex; }
boost::shared_ptr<VertexBuffer> lookup(
const boost::shared_ptr<Array<float> >& array,
)
{
Map::const_iterator it = fMap.find(Key(array, desc));
if (it != fMap.end()) {
// Might return null if the weak_ptr<> is now dangling
// but not yet removed from the map...
boost::shared_ptr<VertexBuffer> ret = it->second.lock();
if (!ret) {
// Get rid of the stalled entry so that insert() can
// work properly.
fMap.erase(it);
}
return ret;
}
else {
return boost::shared_ptr<VertexBuffer>();
}
}
void insert(boost::shared_ptr<VertexBuffer> buffer)
{
fMap.insert(
std::make_pair(
Key(buffer->array(), buffer->descriptor()),
buffer));
}
void removeIfStaled(
const boost::shared_ptr<Array<float> >& array,
)
{
Map::const_iterator it = fMap.find(Key(array, desc));
if (it != fMap.end()) {
// Might return null if the weak_ptr<> is now dangling
// but not yet removed from the map...
boost::shared_ptr<VertexBuffer> ret = it->second.lock();
if (!ret) {
// Get rid of the stalled entry so that insert() can
// work properly.
fMap.erase(it);
}
}
}
size_t nbAllocated()
{ return fMap.size(); }
size_t nbAllocatedBytes()
{
size_t bytes = 0;
BOOST_FOREACH(const Map::value_type& v, fMap) {
boost::shared_ptr<VertexBuffer> buf = v.second.lock();
if (buf) {
bytes += buf->bytes();
}
}
return bytes;
}
private:
typedef boost::unordered_map<
Key,
boost::weak_ptr<VertexBuffer>,
KeyHash,
KeyEqualTo
> Map;
static VertexBufferRegistry fsSingleton;
tbb::mutex fMutex;
Map fMap;
};
VertexBufferRegistry VertexBufferRegistry::fsSingleton;
}
namespace GPUCache {
//==============================================================================
// CLASS ArrayBase
//==============================================================================
void ArrayBase::registerCreationCallback(Callback callback)
{
ArrayBaseImp::registerCreationCallback(callback);
}
void ArrayBase::unregisterCreationCallback(Callback callback)
{
ArrayBaseImp::unregisterCreationCallback(callback);
}
void ArrayBase::registerDestructionCallback(Callback callback)
{
ArrayBaseImp::registerDestructionCallback(callback);
}
void ArrayBase::unregisterDestructionCallback(Callback callback)
{
ArrayBaseImp::unregisterDestructionCallback(callback);
}
ArrayBase::ArrayBase(size_t bytes, const Digest& digest, bool isReadable)
: fKey(bytes, digest)
, fIsReadable(isReadable)
{
ArrayBaseImp::invokeCreationCallback(fKey);
}
ArrayBase::~ArrayBase()
{
ArrayBaseImp::invokeDestructionCallback(fKey);
}
//==============================================================================
// CLASS ArrayReadInterface
//==============================================================================
template class ArrayReadInterface<IndexBuffer::index_t>;
template class ArrayReadInterface<float>;
//==============================================================================
// CLASS Array
//==============================================================================
template <typename T>
Array<T>::~Array()
{
tbb::mutex::scoped_lock lock(ArrayRegistryImp<T>::singleton().mutex());
ArrayRegistryImp<T>::singleton().removeIfStaled(key(), isReadable());
}
template class Array<IndexBuffer::index_t>;
template class Array<float>;
//==============================================================================
// CLASS ReadableArray
//==============================================================================
template class ReadableArray<IndexBuffer::index_t>;
template class ReadableArray<float>;
//==============================================================================
// CLASS ArrayRegistry
//==============================================================================
template <typename T>
tbb::mutex& ArrayRegistry<T>::mutex()
{
return ArrayRegistryImp<T>::singleton().mutex();
}
template <typename T>
boost::shared_ptr<Array<T> > ArrayRegistry<T>::lookup(
const Digest& digest,
size_t size
)
{
boost::shared_ptr<Array<T> > result =
ArrayRegistryImp<T>::singleton().lookup(digest, size);
assert(!result || result->digest() == digest);
assert(!result || result->bytes() == size * sizeof(T));
return result;
}
template <typename T>
boost::shared_ptr<Array<T> > ArrayRegistry<T>::lookupNonReadable(
const Digest& digest,
size_t size
)
{
boost::shared_ptr<Array<T> > result =
ArrayRegistryImp<T>::singleton().lookupNonReadable(digest, size);
assert(!result || result->digest() == digest);
assert(!result || result->bytes() == size * sizeof(T));
return result;
}
template <typename T>
boost::shared_ptr<ReadableArray<T> > ArrayRegistry<T>::lookupReadable(
const Digest& digest,
size_t size
)
{
boost::shared_ptr<ReadableArray<T> > result =
ArrayRegistryImp<T>::singleton().lookupReadable(digest, size);
assert(!result || result->digest() == digest);
assert(!result || result->bytes() == size * sizeof(T));
return result;
}
template <typename T>
void ArrayRegistry<T>::insert(
boost::shared_ptr<Array<T> > array
)
{
ArrayRegistryImp<T>::singleton().insert(array);
}
template class ArrayRegistry<IndexBuffer::index_t>;
template class ArrayRegistry<float>;
//==============================================================================
// CLASS SharedArray
//==============================================================================
template <typename T>
boost::shared_ptr<ReadableArray<T> >
SharedArray<T>::create(
const boost::shared_array<T>& data, size_t size)
{
// Compute the Murmur3 cryptographic hash-key.
Digest digest;
Alembic::Util::MurmurHash3_x64_128(
data.get(), size * sizeof(T), sizeof(T), digest.words);
return create(data, digest, size);
}
template <typename T>
boost::shared_ptr<ReadableArray<T> >
SharedArray<T>::create(
const boost::shared_array<T>& data, Digest digest, size_t size)
{
// We first look if a similar array already exists in the
// cache. If so, we return the cached array to promote sharing as
// much as possible.
boost::shared_ptr<ReadableArray<T> > ret;
{
tbb::mutex::scoped_lock lock(ArrayRegistry<T>::mutex());
ret = ArrayRegistry<T>::lookupReadable(digest, size);
if (!ret) {
ret = boost::make_shared<SharedArray<T> >(
data, size, digest);
ArrayRegistry<T>::insert(ret);
}
}
return ret;
}
template <typename T>
SharedArray<T>::~SharedArray()
{}
template <typename T>
const T* SharedArray<T>::get() const
{
return fData.get();
}
template class SharedArray<IndexBuffer::index_t>;
template class SharedArray<float>;
//==============================================================================
// CLASS IndexBuffer
//==============================================================================
boost::shared_ptr<IndexBuffer> IndexBuffer::create(
const boost::shared_ptr<Array<index_t> >& array,
const size_t beginIdx,
const size_t endIdx
)
{
// We first look if a similar array already exists in the
// cache. If so, we return the cached array to promote sharing as
// much as possible.
boost::shared_ptr<IndexBuffer> ret;
{
tbb::mutex::scoped_lock lock(
IndexBufferRegistry::singleton().mutex());
ret = IndexBufferRegistry::singleton().lookup(
array, beginIdx, endIdx);
if (!ret) {
ret = boost::make_shared<IndexBuffer>(
array, beginIdx, endIdx);
IndexBufferRegistry::singleton().insert(ret);
}
}
return ret;
}
size_t IndexBuffer::nbAllocated()
{
tbb::mutex::scoped_lock lock(
IndexBufferRegistry::singleton().mutex());
return IndexBufferRegistry::singleton().nbAllocated();
}
size_t IndexBuffer::nbAllocatedBytes()
{
tbb::mutex::scoped_lock lock(
IndexBufferRegistry::singleton().mutex());
return IndexBufferRegistry::singleton().nbAllocatedBytes();
}
IndexBuffer::~IndexBuffer()
{
tbb::mutex::scoped_lock lock(
IndexBufferRegistry::singleton().mutex());
IndexBufferRegistry::singleton().removeIfStaled(
fArray, fBeginIdx, fEndIdx);
}
void IndexBuffer::ReplaceArrayInstance(boost::shared_ptr<Array<index_t> >& newArray) const
{
assert(ArrayBase::KeyEqualTo()(fArray->key(), newArray->key()));
if (fArray != newArray) {
boost::shared_ptr<Array<index_t> >& nonConstArray = const_cast<boost::shared_ptr<Array<index_t> >& >(fArray);
nonConstArray = newArray;
}
}
//==============================================================================
// CLASS VertexBuffer
//==============================================================================
boost::shared_ptr<VertexBuffer>
VertexBuffer::createPositions(
const boost::shared_ptr<Array<float> >& array)
{
return create(array,
MString(""),
}
boost::shared_ptr<VertexBuffer>
VertexBuffer::createNormals(
const boost::shared_ptr<Array<float> >& array)
{
return create(array,
MString(""),
}
boost::shared_ptr<VertexBuffer>
VertexBuffer::createUVs(
const boost::shared_ptr<Array<float> >& array)
{
return create( array,
MString("mayaUVIn"),
}
boost::shared_ptr<VertexBuffer>
VertexBuffer::create(
const boost::shared_ptr<Array<float> >& array,
{
// We first look if a similar array already exists in the
// cache. If so, we return the cached array to promote sharing as
// much as possible.
boost::shared_ptr<VertexBuffer> ret;
{
tbb::mutex::scoped_lock lock(
VertexBufferRegistry::singleton().mutex());
ret = VertexBufferRegistry::singleton().lookup(array, desc);
if (!ret) {
ret = boost::make_shared<VertexBuffer>(
array, desc);
VertexBufferRegistry::singleton().insert(ret);
}
}
return ret;
}
size_t VertexBuffer::nbAllocated()
{
tbb::mutex::scoped_lock lock(
VertexBufferRegistry::singleton().mutex());
return VertexBufferRegistry::singleton().nbAllocated();
}
size_t VertexBuffer::nbAllocatedBytes()
{
tbb::mutex::scoped_lock lock(
VertexBufferRegistry::singleton().mutex());
return VertexBufferRegistry::singleton().nbAllocatedBytes();
}
VertexBuffer::~VertexBuffer()
{
tbb::mutex::scoped_lock lock(
VertexBufferRegistry::singleton().mutex());
VertexBufferRegistry::singleton().removeIfStaled(
fArray, fDescriptor);
}
void VertexBuffer::ReplaceArrayInstance(boost::shared_ptr<Array<float> >& newArray) const
{
assert(ArrayBase::KeyEqualTo()(fArray->key(), newArray->key()));
if (fArray != newArray) {
boost::shared_ptr<Array<float> >& nonConstArray = const_cast<boost::shared_ptr<Array<float> >& >(fArray);
nonConstArray = newArray;
}
}
//==============================================================================
// CLASS ShapeSample
//==============================================================================
ShapeSample::ShapeSample(
double timeInSeconds,
size_t numWires,
size_t numVerts,
const boost::shared_ptr<IndexBuffer>& wireVertIndices,
const boost::shared_ptr<IndexBuffer>& triangleVertIndices,
const boost::shared_ptr<VertexBuffer>& positions,
const MBoundingBox& boundingBox,
const MColor& diffuseColor,
bool visibility
)
: fTimeInSeconds(timeInSeconds),
fNumWires(numWires),
fNumVerts(numVerts),
fWireVertIndices(wireVertIndices),
fTriangleVertIndices(
std::vector<boost::shared_ptr<IndexBuffer> >(1, triangleVertIndices)),
fPositions(positions),
fBoundingBox(boundingBox),
fDiffuseColor(diffuseColor),
fVisibility(visibility),
fBoundingBoxPlaceHolder(false)
{
assert( wireVertIndices ? (wireVertIndices->numIndices() == 2 * fNumWires) : (fNumWires == 0) );
assert( positions ? (positions->numVerts() == fNumVerts) : (fNumVerts == 0) );
}
ShapeSample::ShapeSample(
double timeInSeconds,
size_t numWires,
size_t numVerts,
const boost::shared_ptr<IndexBuffer>& wireVertIndices,
const std::vector<boost::shared_ptr<IndexBuffer> >& triangleVertIndices,
const boost::shared_ptr<VertexBuffer>& positions,
const MBoundingBox& boundingBox,
const MColor& diffuseColor,
bool visibility
)
: fTimeInSeconds(timeInSeconds),
fNumWires(numWires),
fNumVerts(numVerts),
fWireVertIndices(wireVertIndices),
fTriangleVertIndices(triangleVertIndices),
fPositions(positions),
fBoundingBox(boundingBox),
fDiffuseColor(diffuseColor),
fVisibility(visibility),
fBoundingBoxPlaceHolder(false)
{
assert( wireVertIndices ? (wireVertIndices->numIndices() == 2 * fNumWires) : (fNumWires == 0) );
assert( positions ? (positions->numVerts() == fNumVerts) : (fNumVerts == 0) );
}
ShapeSample::~ShapeSample()
{}
size_t ShapeSample::numTriangles() const
{
size_t result = 0;
for(size_t i=0; i<numIndexGroups(); ++i) {
result += numTriangles(i);
}
return result;
}
void ShapeSample::setNormals(
const boost::shared_ptr<VertexBuffer>& normals
)
{
assert( !normals || normals->numVerts() == fNumVerts );
fNormals = normals;
}
void ShapeSample::setUVs(
const boost::shared_ptr<VertexBuffer>& uvs
)
{
assert( !uvs || uvs->numVerts() == fNumVerts );
fUVs = uvs;
}
}