OpenSubdiv/opensubdiv/osd/mesh.h
Sergey Sharybin a095ebf1f0 Fix linking error when using GCC and including osd/mesh.h from several places
Te issue here is that some of the functions were not considered templated
anymore because all their template arguments were specified, which made it
so compiler was creating implementation for them in every file from where
the header was included. This causes errors during linking related on the
same symbol implemented in several places.

Marking those functions as inlined solves the problem and should not cause
any bad side effects because they're small enough and likely being inlined
by an optimizer anyway.
2015-07-19 18:06:54 +02:00

618 lines
21 KiB
C++

//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_MESH_H
#define OPENSUBDIV3_OSD_MESH_H
#include "../version.h"
#include <bitset>
#include <cassert>
#include <cstring>
#include <vector>
#include "../far/topologyRefiner.h"
#include "../far/patchTableFactory.h"
#include "../far/stencilTable.h"
#include "../far/stencilTableFactory.h"
#include "../osd/bufferDescriptor.h"
struct ID3D11DeviceContext;
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
enum MeshBits {
MeshAdaptive = 0,
MeshInterleaveVarying = 1,
MeshFVarData = 2,
MeshUseSingleCreasePatch = 3,
MeshEndCapBSplineBasis = 4, // exclusive
MeshEndCapGregoryBasis = 5, // exclusive
MeshEndCapLegacyGregory = 6, // exclusive
NUM_MESH_BITS = 7,
};
typedef std::bitset<NUM_MESH_BITS> MeshBitset;
// ---------------------------------------------------------------------------
template <class PATCH_TABLE>
class MeshInterface {
public:
typedef PATCH_TABLE PatchTable;
typedef typename PatchTable::VertexBufferBinding VertexBufferBinding;
public:
MeshInterface() { }
virtual ~MeshInterface() { }
virtual int GetNumVertices() const = 0;
virtual int GetMaxValence() const = 0;
virtual void UpdateVertexBuffer(float const *vertexData,
int startVertex, int numVerts) = 0;
virtual void UpdateVaryingBuffer(float const *varyingData,
int startVertex, int numVerts) = 0;
virtual void Refine() = 0;
virtual void Synchronize() = 0;
virtual PatchTable * GetPatchTable() const = 0;
virtual Far::PatchTable const *GetFarPatchTable() const = 0;
virtual VertexBufferBinding BindVertexBuffer() = 0;
virtual VertexBufferBinding BindVaryingBuffer() = 0;
protected:
static inline void refineMesh(Far::TopologyRefiner & refiner,
int level, bool adaptive,
bool singleCreasePatch) {
if (adaptive) {
Far::TopologyRefiner::AdaptiveOptions options(level);
options.useSingleCreasePatch = singleCreasePatch;
refiner.RefineAdaptive(options);
} else {
// This dependency on FVar channels should not be necessary
bool fullTopologyInLastLevel = refiner.GetNumFVarChannels()>0;
Far::TopologyRefiner::UniformOptions options(level);
options.fullTopologyInLastLevel = fullTopologyInLastLevel;
refiner.RefineUniform(options);
}
}
};
// ---------------------------------------------------------------------------
template <typename STENCIL_TABLE, typename SRC_STENCIL_TABLE,
typename DEVICE_CONTEXT>
STENCIL_TABLE const *
convertToCompatibleStencilTable(
SRC_STENCIL_TABLE const *table, DEVICE_CONTEXT *context) {
if (not table) return NULL;
return STENCIL_TABLE::Create(table, context);
}
template <>
inline Far::StencilTable const *
convertToCompatibleStencilTable<Far::StencilTable, Far::StencilTable, void>(
Far::StencilTable const *table, void * /*context*/) {
// no need for conversion
// XXX: We don't want to even copy.
if (not table) return NULL;
return new Far::StencilTable(*table);
}
template <>
inline Far::LimitStencilTable const *
convertToCompatibleStencilTable<Far::LimitStencilTable, Far::LimitStencilTable, void>(
Far::LimitStencilTable const *table, void * /*context*/) {
// no need for conversion
// XXX: We don't want to even copy.
if (not table) return NULL;
return new Far::LimitStencilTable(*table);
}
template <>
inline Far::StencilTable const *
convertToCompatibleStencilTable<Far::StencilTable, Far::StencilTable, ID3D11DeviceContext>(
Far::StencilTable const *table, ID3D11DeviceContext * /*context*/) {
// no need for conversion
// XXX: We don't want to even copy.
if (not table) return NULL;
return new Far::StencilTable(*table);
}
// ---------------------------------------------------------------------------
// Osd evaluator cache: for the GPU backends require compiled instance
// (GLXFB, GLCompue, CL)
//
// note: this is just an example usage and client applications are supposed
// to implement their own structure for Evaluator instance.
//
template <typename EVALUATOR>
class EvaluatorCacheT {
public:
~EvaluatorCacheT() {
for(typename Evaluators::iterator it = _evaluators.begin();
it != _evaluators.end(); ++it) {
delete it->evaluator;
}
}
// XXX: FIXME, linear search
struct Entry {
Entry(BufferDescriptor const &srcDesc,
BufferDescriptor const &dstDesc,
BufferDescriptor const &duDesc,
BufferDescriptor const &dvDesc,
EVALUATOR *e) : srcDesc(srcDesc), dstDesc(dstDesc),
duDesc(duDesc), dvDesc(dvDesc), evaluator(e) {}
BufferDescriptor srcDesc, dstDesc, duDesc, dvDesc;
EVALUATOR *evaluator;
};
typedef std::vector<Entry> Evaluators;
template <typename DEVICE_CONTEXT>
EVALUATOR *GetEvaluator(BufferDescriptor const &srcDesc,
BufferDescriptor const &dstDesc,
DEVICE_CONTEXT *deviceContext) {
return GetEvaluator(srcDesc, dstDesc,
BufferDescriptor(),
BufferDescriptor(),
deviceContext);
}
template <typename DEVICE_CONTEXT>
EVALUATOR *GetEvaluator(BufferDescriptor const &srcDesc,
BufferDescriptor const &dstDesc,
BufferDescriptor const &duDesc,
BufferDescriptor const &dvDesc,
DEVICE_CONTEXT *deviceContext) {
for(typename Evaluators::iterator it = _evaluators.begin();
it != _evaluators.end(); ++it) {
if (isEqual(srcDesc, it->srcDesc) &&
isEqual(dstDesc, it->dstDesc) &&
isEqual(duDesc, it->duDesc) &&
isEqual(dvDesc, it->dvDesc)) {
return it->evaluator;
}
}
EVALUATOR *e = EVALUATOR::Create(srcDesc, dstDesc,
duDesc, dvDesc,
deviceContext);
_evaluators.push_back(Entry(srcDesc, dstDesc, duDesc, dvDesc, e));
return e;
}
private:
static bool isEqual(BufferDescriptor const &a,
BufferDescriptor const &b) {
int offsetA = a.stride ? (a.offset % a.stride) : 0;
int offsetB = b.stride ? (b.offset % b.stride) : 0;
// Note: XFB kernel needs to be configured with the local offset
// of the dstDesc to skip preceding primvars.
return (offsetA == offsetB &&
a.length == b.length &&
a.stride == b.stride);
}
Evaluators _evaluators;
};
/// @cond INTERNAL
// template helpers to see if the evaluator is instantiatable or not.
template <typename EVALUATOR>
struct instantiatable
{
typedef char yes[1];
typedef char no[2];
template <typename C> static yes &chk(typename C::Instantiatable *t=0);
template <typename C> static no &chk(...);
static bool const value = sizeof(chk<EVALUATOR>(0)) == sizeof(yes);
};
template <bool C, typename T=void>
struct enable_if { typedef T type; };
template <typename T>
struct enable_if<false, T> { };
/// @endcond
// extract a kernel from cache if available
template <typename EVALUATOR, typename DEVICE_CONTEXT>
static EVALUATOR *GetEvaluator(
EvaluatorCacheT<EVALUATOR> *cache,
BufferDescriptor const &srcDesc,
BufferDescriptor const &dstDesc,
BufferDescriptor const &duDesc,
BufferDescriptor const &dvDesc,
DEVICE_CONTEXT deviceContext,
typename enable_if<instantiatable<EVALUATOR>::value, void>::type*t=0) {
(void)t;
if (cache == NULL) return NULL;
return cache->GetEvaluator(srcDesc, dstDesc, duDesc, dvDesc, deviceContext);
}
template <typename EVALUATOR, typename DEVICE_CONTEXT>
static EVALUATOR *GetEvaluator(
EvaluatorCacheT<EVALUATOR> *cache,
BufferDescriptor const &srcDesc,
BufferDescriptor const &dstDesc,
DEVICE_CONTEXT deviceContext,
typename enable_if<instantiatable<EVALUATOR>::value, void>::type*t=0) {
(void)t;
if (cache == NULL) return NULL;
return cache->GetEvaluator(srcDesc, dstDesc,
BufferDescriptor(),
BufferDescriptor(),
deviceContext);
}
// fallback
template <typename EVALUATOR, typename DEVICE_CONTEXT>
static EVALUATOR *GetEvaluator(
EvaluatorCacheT<EVALUATOR> *,
BufferDescriptor const &,
BufferDescriptor const &,
BufferDescriptor const &,
BufferDescriptor const &,
DEVICE_CONTEXT,
typename enable_if<!instantiatable<EVALUATOR>::value, void>::type*t=0) {
(void)t;
return NULL;
}
template <typename EVALUATOR, typename DEVICE_CONTEXT>
static EVALUATOR *GetEvaluator(
EvaluatorCacheT<EVALUATOR> *,
BufferDescriptor const &,
BufferDescriptor const &,
DEVICE_CONTEXT,
typename enable_if<!instantiatable<EVALUATOR>::value, void>::type*t=0) {
(void)t;
return NULL;
}
// ---------------------------------------------------------------------------
template <typename VERTEX_BUFFER,
typename STENCIL_TABLE,
typename EVALUATOR,
typename PATCH_TABLE,
typename DEVICE_CONTEXT = void>
class Mesh : public MeshInterface<PATCH_TABLE> {
public:
typedef VERTEX_BUFFER VertexBuffer;
typedef EVALUATOR Evaluator;
typedef STENCIL_TABLE StencilTable;
typedef PATCH_TABLE PatchTable;
typedef DEVICE_CONTEXT DeviceContext;
typedef EvaluatorCacheT<Evaluator> EvaluatorCache;
typedef typename PatchTable::VertexBufferBinding VertexBufferBinding;
Mesh(Far::TopologyRefiner * refiner,
int numVertexElements,
int numVaryingElements,
int level,
MeshBitset bits = MeshBitset(),
EvaluatorCache * evaluatorCache = NULL,
DeviceContext * deviceContext = NULL) :
_refiner(refiner),
_farPatchTable(NULL),
_numVertices(0),
_maxValence(0),
_vertexBuffer(NULL),
_varyingBuffer(NULL),
_vertexStencilTable(NULL),
_varyingStencilTable(NULL),
_evaluatorCache(evaluatorCache),
_patchTable(NULL),
_deviceContext(deviceContext) {
assert(_refiner);
MeshInterface<PATCH_TABLE>::refineMesh(
*_refiner, level,
bits.test(MeshAdaptive),
bits.test(MeshUseSingleCreasePatch));
int vertexBufferStride = numVertexElements +
(bits.test(MeshInterleaveVarying) ? numVaryingElements : 0);
int varyingBufferStride =
(bits.test(MeshInterleaveVarying) ? 0 : numVaryingElements);
initializeContext(numVertexElements,
numVaryingElements,
level, bits);
initializeVertexBuffers(_numVertices,
vertexBufferStride,
varyingBufferStride);
// configure vertex buffer descriptor
_vertexDesc =
BufferDescriptor(0, numVertexElements, vertexBufferStride);
if (bits.test(MeshInterleaveVarying)) {
_varyingDesc = BufferDescriptor(
numVertexElements, numVaryingElements, vertexBufferStride);
} else {
_varyingDesc = BufferDescriptor(
0, numVaryingElements, varyingBufferStride);
}
}
virtual ~Mesh() {
delete _refiner;
delete _farPatchTable;
delete _vertexBuffer;
delete _varyingBuffer;
delete _vertexStencilTable;
delete _varyingStencilTable;
delete _patchTable;
// deviceContext and evaluatorCache are not owned by this class.
}
virtual void UpdateVertexBuffer(float const *vertexData,
int startVertex, int numVerts) {
_vertexBuffer->UpdateData(vertexData, startVertex, numVerts,
_deviceContext);
}
virtual void UpdateVaryingBuffer(float const *varyingData,
int startVertex, int numVerts) {
_varyingBuffer->UpdateData(varyingData, startVertex, numVerts,
_deviceContext);
}
virtual void Refine() {
int numControlVertices = _refiner->GetLevel(0).GetNumVertices();
BufferDescriptor srcDesc = _vertexDesc;
BufferDescriptor dstDesc(srcDesc);
dstDesc.offset += numControlVertices * dstDesc.stride;
// note that the _evaluatorCache can be NULL and thus
// the evaluatorInstance can be NULL
// (for uninstantiatable kernels CPU,TBB etc)
Evaluator const *instance = GetEvaluator<Evaluator>(
_evaluatorCache, srcDesc, dstDesc,
_deviceContext);
Evaluator::EvalStencils(_vertexBuffer, srcDesc,
_vertexBuffer, dstDesc,
_vertexStencilTable,
instance, _deviceContext);
if (_varyingDesc.length > 0) {
BufferDescriptor srcDesc = _varyingDesc;
BufferDescriptor dstDesc(srcDesc);
dstDesc.offset += numControlVertices * dstDesc.stride;
instance = GetEvaluator<Evaluator>(
_evaluatorCache, srcDesc, dstDesc,
_deviceContext);
if (_varyingBuffer) {
// non-interleaved
Evaluator::EvalStencils(_varyingBuffer, srcDesc,
_varyingBuffer, dstDesc,
_varyingStencilTable,
instance, _deviceContext);
} else {
// interleaved
Evaluator::EvalStencils(_vertexBuffer, srcDesc,
_vertexBuffer, dstDesc,
_varyingStencilTable,
instance, _deviceContext);
}
}
}
virtual void Synchronize() {
Evaluator::Synchronize(_deviceContext);
}
virtual PatchTable * GetPatchTable() const {
return _patchTable;
}
virtual Far::PatchTable const *GetFarPatchTable() const {
return _farPatchTable;
}
virtual int GetNumVertices() const { return _numVertices; }
virtual int GetMaxValence() const { return _maxValence; }
virtual VertexBufferBinding BindVertexBuffer() {
return _vertexBuffer->BindVBO(_deviceContext);
}
virtual VertexBufferBinding BindVaryingBuffer() {
return _varyingBuffer->BindVBO(_deviceContext);
}
virtual VertexBuffer * GetVertexBuffer() {
return _vertexBuffer;
}
virtual VertexBuffer * GetVaryingBuffer() {
return _varyingBuffer;
}
virtual Far::TopologyRefiner const * GetTopologyRefiner() const {
return _refiner;
}
private:
void initializeContext(int numVertexElements,
int numVaryingElements,
int level, MeshBitset bits) {
assert(_refiner);
Far::StencilTableFactory::Options options;
options.generateOffsets = true;
options.generateIntermediateLevels =
_refiner->IsUniform() ? false : true;
Far::StencilTable const * vertexStencils = NULL;
Far::StencilTable const * varyingStencils = NULL;
if (numVertexElements>0) {
vertexStencils = Far::StencilTableFactory::Create(*_refiner,
options);
}
if (numVaryingElements>0) {
options.interpolationMode =
Far::StencilTableFactory::INTERPOLATE_VARYING;
varyingStencils = Far::StencilTableFactory::Create(*_refiner,
options);
}
Far::PatchTableFactory::Options poptions(level);
poptions.generateFVarTables = bits.test(MeshFVarData);
poptions.useSingleCreasePatch = bits.test(MeshUseSingleCreasePatch);
if (bits.test(MeshEndCapBSplineBasis)) {
poptions.SetEndCapType(
Far::PatchTableFactory::Options::ENDCAP_BSPLINE_BASIS);
} else if (bits.test(MeshEndCapGregoryBasis)) {
poptions.SetEndCapType(
Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS);
// points on gregory basis endcap boundary can be shared among
// adjacent patches to save some stencils.
poptions.shareEndCapPatchPoints = true;
} else if (bits.test(MeshEndCapLegacyGregory)) {
poptions.SetEndCapType(
Far::PatchTableFactory::Options::ENDCAP_LEGACY_GREGORY);
}
_farPatchTable = Far::PatchTableFactory::Create(*_refiner, poptions);
// if there's endcap stencils, merge it into regular stencils.
if (_farPatchTable->GetLocalPointStencilTable()) {
// append stencils
if (Far::StencilTable const *vertexStencilsWithLocalPoints =
Far::StencilTableFactory::AppendLocalPointStencilTable(
*_refiner,
vertexStencils,
_farPatchTable->GetLocalPointStencilTable())) {
delete vertexStencils;
vertexStencils = vertexStencilsWithLocalPoints;
}
if (varyingStencils) {
if (Far::StencilTable const *varyingStencilsWithLocalPoints =
Far::StencilTableFactory::AppendLocalPointStencilTable(
*_refiner,
varyingStencils,
_farPatchTable->GetLocalPointVaryingStencilTable())) {
delete varyingStencils;
varyingStencils = varyingStencilsWithLocalPoints;
}
}
}
_maxValence = _farPatchTable->GetMaxValence();
_patchTable = PatchTable::Create(_farPatchTable, _deviceContext);
// numvertices = coarse verts + refined verts + gregory basis verts
_numVertices = vertexStencils->GetNumControlVertices()
+ vertexStencils->GetNumStencils();
// convert to device stenciltable if necessary.
_vertexStencilTable =
convertToCompatibleStencilTable<StencilTable>(
vertexStencils, _deviceContext);
_varyingStencilTable =
convertToCompatibleStencilTable<StencilTable>(
varyingStencils, _deviceContext);
// FIXME: we do extra copyings for Far::Stencils.
delete vertexStencils;
delete varyingStencils;
}
void initializeVertexBuffers(int numVertices,
int numVertexElements,
int numVaryingElements) {
if (numVertexElements) {
_vertexBuffer = VertexBuffer::Create(numVertexElements,
numVertices, _deviceContext);
}
if (numVaryingElements) {
_varyingBuffer = VertexBuffer::Create(numVaryingElements,
numVertices, _deviceContext);
}
}
Far::TopologyRefiner * _refiner;
Far::PatchTable * _farPatchTable;
int _numVertices;
int _maxValence;
VertexBuffer * _vertexBuffer;
VertexBuffer * _varyingBuffer;
BufferDescriptor _vertexDesc;
BufferDescriptor _varyingDesc;
StencilTable const * _vertexStencilTable;
StencilTable const * _varyingStencilTable;
EvaluatorCache * _evaluatorCache;
PatchTable *_patchTable;
DeviceContext *_deviceContext;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_MESH_H