OpenSubdiv/opensubdiv/osd/mesh.h
Takahito Tejima 109a3f5383 osd layer: Add GLPatchTable and retire DrawContext
In osd layer, we use GLPatchTable (D3D11PatchTable) as a
device-specific representation of FarPatchTables instead of
DrawContext. GLPatchTable may be used not only for drawing
but also for GPU eval APIs (not yet supported though.
We may add CudaPatchTable etc as needed).

The legacy gregory patch drawing buffers are carved out to
the separate class, named GLLegacyGregoryPatchTable.

Also face-varying data are split into client side for now, until
we add new and more robust face-varying drawing structure
(scheduled at 3.1 release)

Tentatively replicate PatchArray structure in GLPatchTables. It will
be revised in the upcoming change.

Shifting hard-coded SRV locations of legacy gregory buffers in HLSL shaders.
2015-05-20 15:55:06 -07:00

540 lines
18 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/patchTablesFactory.h"
#include "../far/stencilTables.h"
#include "../far/stencilTablesFactory.h"
#include "../osd/vertexDescriptor.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::PatchTables const *GetFarPatchTables() 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_TABLES, typename DEVICE_CONTEXT>
STENCIL_TABLES const *
convertToCompatibleStencilTables(
Far::StencilTables const *table, DEVICE_CONTEXT *context) {
if (not table) return NULL;
return STENCIL_TABLES::Create(table, context);
}
template <>
Far::StencilTables const *
convertToCompatibleStencilTables<Far::StencilTables, void>(
Far::StencilTables const *table, void * /*context*/) {
// no need for conversion
// XXX: We don't want to even copy.
if (not table) return NULL;
return new Far::StencilTables(*table);
}
template <>
Far::StencilTables const *
convertToCompatibleStencilTables<Far::StencilTables, ID3D11DeviceContext>(
Far::StencilTables const *table, ID3D11DeviceContext * /*context*/) {
// no need for conversion
// XXX: We don't want to even copy.
if (not table) return NULL;
return new Far::StencilTables(*table);
}
// ---------------------------------------------------------------------------
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(VertexBufferDescriptor const &sd,
VertexBufferDescriptor const &dd,
EVALUATOR *e) : srcDesc(sd), dstDesc(dd), evaluator(e) {}
VertexBufferDescriptor srcDesc, dstDesc;
EVALUATOR *evaluator;
};
typedef std::vector<Entry> Evaluators;
template <typename DEVICE_CONTEXT>
EVALUATOR *GetEvaluator(VertexBufferDescriptor const &srcDesc,
VertexBufferDescriptor const &dstDesc,
DEVICE_CONTEXT *deviceContext) {
for(typename Evaluators::iterator it = _evaluators.begin();
it != _evaluators.end(); ++it) {
if (it->srcDesc.length == srcDesc.length and
it->srcDesc.stride == srcDesc.stride and
it->dstDesc.length == dstDesc.length and
it->dstDesc.stride == dstDesc.stride) {
return it->evaluator;
}
}
EVALUATOR *e = EVALUATOR::Create(srcDesc, dstDesc, deviceContext);
_evaluators.push_back(Entry(srcDesc, dstDesc, e));
return e;
}
private:
Evaluators _evaluators;
};
// 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> { };
// extract a kernel from cache if available
template <typename EVALUATOR, typename DEVICE_CONTEXT>
static EVALUATOR *GetEvaluator(
EvaluatorCacheT<EVALUATOR> *cache,
VertexBufferDescriptor const &srcDesc,
VertexBufferDescriptor 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, deviceContext);
}
// fallback
template <typename EVALUATOR, typename DEVICE_CONTEXT>
static EVALUATOR *GetEvaluator(
EvaluatorCacheT<EVALUATOR> *,
VertexBufferDescriptor const &,
VertexBufferDescriptor const &,
DEVICE_CONTEXT,
typename enable_if<!instantiatable<EVALUATOR>::value, void>::type*t=0) {
(void)t;
return NULL;
}
// ---------------------------------------------------------------------------
template <typename VERTEX_BUFFER,
typename STENCIL_TABLES,
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_TABLES StencilTables;
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),
_farPatchTables(NULL),
_numVertices(0),
_maxValence(0),
_vertexBuffer(NULL),
_varyingBuffer(NULL),
_vertexStencilTables(NULL),
_varyingStencilTables(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 = VertexBufferDescriptor(0,
numVertexElements,
vertexBufferStride);
if (bits.test(MeshInterleaveVarying)) {
_varyingDesc = VertexBufferDescriptor(numVertexElements,
numVaryingElements,
vertexBufferStride);
} else {
_varyingDesc = VertexBufferDescriptor(0,
numVaryingElements,
varyingBufferStride);
}
}
virtual ~Mesh() {
delete _refiner;
delete _farPatchTables;
delete _vertexBuffer;
delete _varyingBuffer;
delete _vertexStencilTables;
delete _varyingStencilTables;
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->GetNumVertices(0);
VertexBufferDescriptor srcDesc = _vertexDesc;
VertexBufferDescriptor 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,
_vertexStencilTables,
instance, _deviceContext);
if (_varyingDesc.length > 0) {
VertexBufferDescriptor srcDesc = _varyingDesc;
VertexBufferDescriptor dstDesc(srcDesc);
dstDesc.offset += numControlVertices * dstDesc.stride;
instance = GetEvaluator<Evaluator>(
_evaluatorCache, srcDesc, dstDesc, _deviceContext);
if (_varyingBuffer) {
// non-interleaved
Evaluator::EvalStencils(_varyingBuffer, srcDesc,
_varyingBuffer, dstDesc,
_varyingStencilTables,
instance, _deviceContext);
} else {
// interleaved
Evaluator::EvalStencils(_vertexBuffer, srcDesc,
_vertexBuffer, dstDesc,
_varyingStencilTables,
instance, _deviceContext);
}
}
}
virtual void Synchronize() {
Evaluator::Synchronize(_deviceContext);
}
virtual PatchTable * GetPatchTable() const {
return _patchTable;
}
virtual Far::PatchTables const *GetFarPatchTables() const {
return _farPatchTables;
}
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::StencilTablesFactory::Options options;
options.generateOffsets = true;
options.generateIntermediateLevels =
_refiner->IsUniform() ? false : true;
Far::StencilTables const * vertexStencils = NULL;
Far::StencilTables const * varyingStencils = NULL;
if (numVertexElements>0) {
vertexStencils = Far::StencilTablesFactory::Create(*_refiner,
options);
}
if (numVaryingElements>0) {
options.interpolationMode =
Far::StencilTablesFactory::INTERPOLATE_VARYING;
varyingStencils = Far::StencilTablesFactory::Create(*_refiner,
options);
}
Far::PatchTablesFactory::Options poptions(level);
poptions.generateFVarTables = bits.test(MeshFVarData);
poptions.useSingleCreasePatch = bits.test(MeshUseSingleCreasePatch);
if (bits.test(MeshEndCapBSplineBasis)) {
poptions.SetEndCapType(
Far::PatchTablesFactory::Options::ENDCAP_BSPLINE_BASIS);
} else if (bits.test(MeshEndCapGregoryBasis)) {
poptions.SetEndCapType(
Far::PatchTablesFactory::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::PatchTablesFactory::Options::ENDCAP_LEGACY_GREGORY);
}
_farPatchTables = Far::PatchTablesFactory::Create(*_refiner, poptions);
// if there's endcap stencils, merge it into regular stencils.
if (_farPatchTables->GetEndCapVertexStencilTables()) {
// append stencils
if (Far::StencilTables const *vertexStencilsWithEndCap =
Far::StencilTablesFactory::AppendEndCapStencilTables(
*_refiner,
vertexStencils,
_farPatchTables->GetEndCapVertexStencilTables())) {
delete vertexStencils;
vertexStencils = vertexStencilsWithEndCap;
}
if (varyingStencils) {
if (Far::StencilTables const *varyingStencilsWithEndCap =
Far::StencilTablesFactory::AppendEndCapStencilTables(
*_refiner,
varyingStencils,
_farPatchTables->GetEndCapVaryingStencilTables())) {
delete varyingStencils;
varyingStencils = varyingStencilsWithEndCap;
}
}
}
_maxValence = _farPatchTables->GetMaxValence();
_patchTable = PatchTable::Create(_farPatchTables, _deviceContext);
// numvertices = coarse verts + refined verts + gregory basis verts
_numVertices = vertexStencils->GetNumControlVertices()
+ vertexStencils->GetNumStencils();
// convert to device stenciltables if necessary.
_vertexStencilTables =
convertToCompatibleStencilTables<StencilTables>(
vertexStencils, _deviceContext);
_varyingStencilTables =
convertToCompatibleStencilTables<StencilTables>(
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::PatchTables * _farPatchTables;
int _numVertices;
int _maxValence;
VertexBuffer * _vertexBuffer;
VertexBuffer * _varyingBuffer;
VertexBufferDescriptor _vertexDesc;
VertexBufferDescriptor _varyingDesc;
StencilTables const * _vertexStencilTables;
StencilTables const * _varyingStencilTables;
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