OpenSubdiv/opensubdiv/far/stencilBuilder.cpp
barry 647039a8ee Major public addition of class templates for multiple precision:
- converted classes for stencils, tables and factories to templates
    - added backward compatible class wrappers around new templates
    - extended PatchTable to support variable precision stencil tables
    - extended PatchTableBuilder to manage PatchTable's new stencil tables
    - templated PatchTableBuilder conversion from SparseMatrix to stencils
    - extended public PatchTableFactory interface to specify precision
    - converted PrimvarRefiner class to template and added wrapper class
2018-07-28 14:56:43 -07:00

607 lines
19 KiB
C++

//
// Copyright 2015 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.
//
#include "../far/stencilBuilder.h"
#include "../far/topologyRefiner.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
namespace {
#ifdef __INTEL_COMPILER
#pragma warning (push)
#pragma warning disable 1572
#endif
template <typename REAL>
inline bool isWeightZero(REAL w) { return (w == (REAL)0.0); }
#ifdef __INTEL_COMPILER
#pragma warning (pop)
#endif
}
template <typename REAL>
struct Point1stDerivWeight {
REAL p;
REAL du;
REAL dv;
Point1stDerivWeight()
: p(0.0f), du(0.0f), dv(0.0f)
{ }
Point1stDerivWeight(REAL w)
: p(w), du(w), dv(w)
{ }
Point1stDerivWeight(REAL w, REAL wDu, REAL wDv)
: p(w), du(wDu), dv(wDv)
{ }
friend Point1stDerivWeight operator*(Point1stDerivWeight lhs,
Point1stDerivWeight const& rhs) {
lhs.p *= rhs.p;
lhs.du *= rhs.du;
lhs.dv *= rhs.dv;
return lhs;
}
Point1stDerivWeight& operator+=(Point1stDerivWeight const& rhs) {
p += rhs.p;
du += rhs.du;
dv += rhs.dv;
return *this;
}
};
template <typename REAL>
struct Point2ndDerivWeight {
REAL p;
REAL du;
REAL dv;
REAL duu;
REAL duv;
REAL dvv;
Point2ndDerivWeight()
: p(0.0f), du(0.0f), dv(0.0f), duu(0.0f), duv(0.0f), dvv(0.0f)
{ }
Point2ndDerivWeight(REAL w)
: p(w), du(w), dv(w), duu(w), duv(w), dvv(w)
{ }
Point2ndDerivWeight(REAL w, REAL wDu, REAL wDv,
REAL wDuu, REAL wDuv, REAL wDvv)
: p(w), du(wDu), dv(wDv), duu(wDuu), duv(wDuv), dvv(wDvv)
{ }
friend Point2ndDerivWeight operator*(Point2ndDerivWeight lhs,
Point2ndDerivWeight const& rhs) {
lhs.p *= rhs.p;
lhs.du *= rhs.du;
lhs.dv *= rhs.dv;
lhs.duu *= rhs.duu;
lhs.duv *= rhs.duv;
lhs.dvv *= rhs.dvv;
return lhs;
}
Point2ndDerivWeight& operator+=(Point2ndDerivWeight const& rhs) {
p += rhs.p;
du += rhs.du;
dv += rhs.dv;
duu += rhs.duu;
duv += rhs.duv;
dvv += rhs.dvv;
return *this;
}
};
/// Stencil table constructor set.
///
template <typename REAL>
class WeightTable {
public:
WeightTable(int coarseVerts,
bool genCtrlVertStencils,
bool compactWeights)
: _size(0)
, _lastOffset(0)
, _coarseVertCount(coarseVerts)
, _compactWeights(compactWeights)
{
// These numbers were chosen by profiling production assets at uniform
// level 3.
size_t n = std::max(coarseVerts,
std::min(int(5*1024*1024),
coarseVerts*2));
_dests.reserve(n);
_sources.reserve(n);
_weights.reserve(n);
if (!genCtrlVertStencils)
return;
// Generate trivial control vert stencils
_sources.resize(coarseVerts);
_weights.resize(coarseVerts);
_dests.resize(coarseVerts);
_indices.resize(coarseVerts);
_sizes.resize(coarseVerts);
for (int i = 0; i < coarseVerts; i++) {
_indices[i] = i;
_sizes[i] = 1;
_dests[i] = i;
_sources[i] = i;
_weights[i] = 1.0;
}
_size = static_cast<int>(_sources.size());
_lastOffset = _size - 1;
}
template <class W, class WACCUM>
void AddWithWeight(int src, int dest, W weight, WACCUM weights)
{
// Factorized stencils are expressed purely in terms of the control
// mesh verts. Without this flattening, level_i's weights would point
// to level_i-1, which would point to level_i-2, until the final level
// points to the control verts.
//
// So here, we check if the incoming vert (src) is in the control mesh,
// if it is, we can simply merge it without attempting to resolve it
// first.
if (src < _coarseVertCount) {
merge(src, dest, weight, W(1.0), _lastOffset, _size, weights);
return;
}
// src is not in the control mesh, so resolve all contributing coarse
// verts (src itself is made up of many control vert weights).
//
// Find the src stencil and number of contributing CVs.
int len = _sizes[src];
int start = _indices[src];
for (int i = start; i < start+len; i++) {
// Invariant: by processing each level in order and each vertex in
// dependent order, any src stencil vertex reference is guaranteed
// to consist only of coarse verts: therefore resolving src verts
// must yield verts in the coarse mesh.
assert(_sources[i] < _coarseVertCount);
// Merge each of src's contributing verts into this stencil.
merge(_sources[i], dest, weights.Get(i), weight,
_lastOffset, _size, weights);
}
}
class Point1stDerivAccumulator {
WeightTable* _tbl;
public:
Point1stDerivAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(Point1stDerivWeight<REAL> weight) {
_tbl->_weights.push_back(weight.p);
_tbl->_duWeights.push_back(weight.du);
_tbl->_dvWeights.push_back(weight.dv);
}
void Add(size_t i, Point1stDerivWeight<REAL> weight) {
_tbl->_weights[i] += weight.p;
_tbl->_duWeights[i] += weight.du;
_tbl->_dvWeights[i] += weight.dv;
}
Point1stDerivWeight<REAL> Get(size_t index) {
return Point1stDerivWeight<REAL>(_tbl->_weights[index],
_tbl->_duWeights[index],
_tbl->_dvWeights[index]);
}
};
Point1stDerivAccumulator GetPoint1stDerivAccumulator() {
return Point1stDerivAccumulator(this);
};
class Point2ndDerivAccumulator {
WeightTable* _tbl;
public:
Point2ndDerivAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(Point2ndDerivWeight<REAL> weight) {
_tbl->_weights.push_back(weight.p);
_tbl->_duWeights.push_back(weight.du);
_tbl->_dvWeights.push_back(weight.dv);
_tbl->_duuWeights.push_back(weight.duu);
_tbl->_duvWeights.push_back(weight.duv);
_tbl->_dvvWeights.push_back(weight.dvv);
}
void Add(size_t i, Point2ndDerivWeight<REAL> weight) {
_tbl->_weights[i] += weight.p;
_tbl->_duWeights[i] += weight.du;
_tbl->_dvWeights[i] += weight.dv;
_tbl->_duuWeights[i] += weight.duu;
_tbl->_duvWeights[i] += weight.duv;
_tbl->_dvvWeights[i] += weight.dvv;
}
Point2ndDerivWeight<REAL> Get(size_t index) {
return Point2ndDerivWeight<REAL>(_tbl->_weights[index],
_tbl->_duWeights[index],
_tbl->_dvWeights[index],
_tbl->_duuWeights[index],
_tbl->_duvWeights[index],
_tbl->_dvvWeights[index]);
}
};
Point2ndDerivAccumulator GetPoint2ndDerivAccumulator() {
return Point2ndDerivAccumulator(this);
};
class ScalarAccumulator {
WeightTable* _tbl;
public:
ScalarAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(REAL weight) {
_tbl->_weights.push_back(weight);
}
void Add(size_t i, REAL w) {
_tbl->_weights[i] += w;
}
REAL Get(size_t index) {
return _tbl->_weights[index];
}
};
ScalarAccumulator GetScalarAccumulator() {
return ScalarAccumulator(this);
};
std::vector<int> const&
GetOffsets() const { return _indices; }
std::vector<int> const&
GetSizes() const { return _sizes; }
std::vector<int> const&
GetSources() const { return _sources; }
std::vector<REAL> const&
GetWeights() const { return _weights; }
std::vector<REAL> const&
GetDuWeights() const { return _duWeights; }
std::vector<REAL> const&
GetDvWeights() const { return _dvWeights; }
std::vector<REAL> const&
GetDuuWeights() const { return _duuWeights; }
std::vector<REAL> const&
GetDuvWeights() const { return _duvWeights; }
std::vector<REAL> const&
GetDvvWeights() const { return _dvvWeights; }
void SetCoarseVertCount(int numVerts) {
_coarseVertCount = numVerts;
}
private:
// Merge a vertex weight into the stencil table, if there is an existing
// weight for a given source vertex it will be combined.
//
// PERFORMANCE: caution, this function is super hot.
template <class W, class WACCUM>
void merge(int src, int dst, W weight,
// Delaying weight*factor multiplication hides memory latency of
// accessing weight[i], yielding more stable performance.
W weightFactor,
// Similarly, passing offset & tableSize as params yields higher
// performance than accessing the class members directly.
int lastOffset, int tableSize, WACCUM weights)
{
// The lastOffset is the vertex we're currently processing, by
// leveraging this we need not lookup the dest stencil size or offset.
//
// Additionally, if the client does not want the resulting verts
// compacted, do not attempt to combine weights.
if (_compactWeights && !_dests.empty() && _dests[lastOffset] == dst) {
// tableSize is exactly _sources.size(), but using tableSize is
// significantly faster.
for (int i = lastOffset; i < tableSize; i++) {
// If we find an existing vertex that matches src, we need to
// combine the weights to avoid duplicate entries for src.
if (_sources[i] == src) {
weights.Add(i, weight*weightFactor);
return;
}
}
}
// We haven't seen src yet, insert it as a new vertex weight.
add(src, dst, weight*weightFactor, weights);
}
// Add a new vertex weight to the stencil table.
template <class W, class WACCUM>
void add(int src, int dst, W weight, WACCUM weights)
{
// The _dests array has num(weights) elements mapping each individual
// element back to a specific stencil. The array is constructed in such
// a way that the current stencil being built is always at the end of
// the array, so if the dests array is empty or back() doesn't match
// dst, then we just started building a new stencil.
if (_dests.empty() || dst != _dests.back()) {
// _indices and _sizes always have num(stencils) elements so that
// stencils can be directly looked up by their index in these
// arrays. So here, ensure that they are large enough to hold the
// new stencil about to be built.
if (dst+1 > (int)_indices.size()) {
_indices.resize(dst+1);
_sizes.resize(dst+1);
}
// Initialize the new stencil's meta-data (offset, size).
_indices[dst] = static_cast<int>(_sources.size());
_sizes[dst] = 0;
// Keep track of where the current stencil begins, which lets us
// avoid having to look it up later.
_lastOffset = static_cast<int>(_sources.size());
}
// Cache the number of elements as an optimization, it's faster than
// calling size() on any of the vectors.
_size++;
// Increment the current stencil element size.
_sizes[dst]++;
// Track this element as belonging to the stencil "dst".
_dests.push_back(dst);
// Store the actual stencil data.
_sources.push_back(src);
weights.PushBack(weight);
}
// The following vectors are explicitly stored as non-interleaved elements
// to reduce cache misses.
// Stencil to destination vertex map.
std::vector<int> _dests;
// The actual stencil data.
std::vector<int> _sources;
std::vector<REAL> _weights;
std::vector<REAL> _duWeights;
std::vector<REAL> _dvWeights;
std::vector<REAL> _duuWeights;
std::vector<REAL> _duvWeights;
std::vector<REAL> _dvvWeights;
// Index data used to recover stencil-to-vertex mapping.
std::vector<int> _indices;
std::vector<int> _sizes;
// Acceleration members to avoid pointer chasing and reverse loops.
int _size;
int _lastOffset;
int _coarseVertCount;
bool _compactWeights;
};
template <typename REAL>
StencilBuilder<REAL>::StencilBuilder(int coarseVertCount,
bool genCtrlVertStencils,
bool compactWeights)
: _weightTable(new WeightTable<REAL>(coarseVertCount,
genCtrlVertStencils,
compactWeights))
{
}
template <typename REAL>
StencilBuilder<REAL>::~StencilBuilder()
{
delete _weightTable;
}
template <typename REAL>
size_t
StencilBuilder<REAL>::GetNumVerticesTotal() const
{
return _weightTable->GetWeights().size();
}
template <typename REAL>
int
StencilBuilder<REAL>::GetNumVertsInStencil(size_t stencilIndex) const
{
if (stencilIndex > _weightTable->GetSizes().size() - 1)
return 0;
return (int)_weightTable->GetSizes()[stencilIndex];
}
template <typename REAL>
void
StencilBuilder<REAL>::SetCoarseVertCount(int numVerts)
{
_weightTable->SetCoarseVertCount(numVerts);
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilOffsets() const {
return _weightTable->GetOffsets();
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilSizes() const {
return _weightTable->GetSizes();
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilSources() const {
return _weightTable->GetSources();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilWeights() const {
return _weightTable->GetWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuWeights() const {
return _weightTable->GetDuWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDvWeights() const {
return _weightTable->GetDvWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuuWeights() const {
return _weightTable->GetDuuWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuvWeights() const {
return _weightTable->GetDuvWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDvvWeights() const {
return _weightTable->GetDvvWeights();
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(Index const & src, REAL weight)
{
// Ignore no-op weights.
if (isWeightZero(weight)) {
return;
}
_owner->_weightTable->AddWithWeight(src._index, _index, weight,
_owner->_weightTable->GetScalarAccumulator());
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src, REAL weight)
{
if (isWeightZero(weight)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
REAL wgt = weight * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetScalarAccumulator());
}
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv)
{
if (isWeightZero(weight) && isWeightZero(du) && isWeightZero(dv)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
Point1stDerivWeight<REAL> wgt = Point1stDerivWeight<REAL>(weight, du, dv) * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetPoint1stDerivAccumulator());
}
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv, REAL duu, REAL duv, REAL dvv)
{
if (isWeightZero(weight) && isWeightZero(du) && isWeightZero(dv) &&
isWeightZero(duu) && isWeightZero(duv) && isWeightZero(dvv)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
Point2ndDerivWeight<REAL> wgt = Point2ndDerivWeight<REAL>(weight, du, dv, duu, duv, dvv) * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetPoint2ndDerivAccumulator());
}
}
template class StencilBuilder<float>;
template class StencilBuilder<double>;
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv