// // 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 { struct PointDerivWeight { float p; float du; float dv; PointDerivWeight() : p(0.0f), du(0.0f), dv(0.0f) { } PointDerivWeight(float w) : p(w), du(w), dv(w) { } PointDerivWeight(float w, float wDu, float wDv) : p(w), du(wDu), dv(wDv) { } friend PointDerivWeight operator*(PointDerivWeight lhs, PointDerivWeight const& rhs) { lhs.p *= rhs.p; lhs.du *= rhs.du; lhs.dv *= rhs.dv; return lhs; } PointDerivWeight& operator+=(PointDerivWeight const& rhs) { p += rhs.p; du += rhs.du; dv += rhs.dv; return *this; } }; /// Stencil table constructor set. /// 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(_sources.size()); _lastOffset = _size - 1; } template 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 PointDerivAccumulator { WeightTable* _tbl; public: PointDerivAccumulator(WeightTable* tbl) : _tbl(tbl) { } void PushBack(PointDerivWeight weight) { _tbl->_weights.push_back(weight.p); _tbl->_duWeights.push_back(weight.du); _tbl->_dvWeights.push_back(weight.dv); } void Add(size_t i, PointDerivWeight weight) { _tbl->_weights[i] += weight.p; _tbl->_duWeights[i] += weight.du; _tbl->_dvWeights[i] += weight.dv; } PointDerivWeight Get(size_t index) { return PointDerivWeight(_tbl->_weights[index], _tbl->_duWeights[index], _tbl->_dvWeights[index]); } }; PointDerivAccumulator GetPointDerivAccumulator() { return PointDerivAccumulator(this); }; class ScalarAccumulator { WeightTable* _tbl; public: ScalarAccumulator(WeightTable* tbl) : _tbl(tbl) { } void PushBack(PointDerivWeight weight) { _tbl->_weights.push_back(weight.p); } void Add(size_t i, float w) { _tbl->_weights[i] += w; } float Get(size_t index) { return _tbl->_weights[index]; } }; ScalarAccumulator GetScalarAccumulator() { return ScalarAccumulator(this); }; std::vector const& GetOffsets() const { return _indices; } std::vector const& GetSizes() const { return _sizes; } std::vector const& GetSources() const { return _sources; } std::vector const& GetWeights() const { return _weights; } std::vector const& GetDuWeights() const { return _duWeights; } std::vector const& GetDvWeights() const { return _dvWeights; } 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 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 and _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 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() or 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(_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(_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 _dests; // The actual stencil data. std::vector _sources; std::vector _weights; std::vector _duWeights; std::vector _dvWeights; // Index data used to recover stencil-to-vertex mapping. std::vector _indices; std::vector _sizes; // Acceleration members to avoid pointer chasing and reverse loops. int _size; int _lastOffset; int _coarseVertCount; bool _compactWeights; }; StencilBuilder::StencilBuilder(int coarseVertCount, bool isVarying, bool genCtrlVertStencils, bool compactWeights) : _weightTable(new WeightTable(coarseVertCount, genCtrlVertStencils, compactWeights)) , _isVarying(isVarying) { } StencilBuilder::~StencilBuilder() { delete _weightTable; } size_t StencilBuilder::GetNumVerticesTotal() const { return _weightTable->GetWeights().size(); } int StencilBuilder::GetNumVertsInStencil(size_t stencilIndex) const { if (stencilIndex > _weightTable->GetSizes().size() - 1) return 0; return (int)_weightTable->GetSizes()[stencilIndex]; } std::vector const& StencilBuilder::GetStencilOffsets() const { return _weightTable->GetOffsets(); } std::vector const& StencilBuilder::GetStencilSizes() const { return _weightTable->GetSizes(); } std::vector const& StencilBuilder::GetStencilSources() const { return _weightTable->GetSources(); } std::vector const& StencilBuilder::GetStencilWeights() const { return _weightTable->GetWeights(); } std::vector const& StencilBuilder::GetStencilDuWeights() const { return _weightTable->GetDuWeights(); } std::vector const& StencilBuilder::GetStencilDvWeights() const { return _weightTable->GetDvWeights(); } void StencilBuilder::Index::AddWithWeight(Index const & src, float weight) { if (_owner->_isVarying) return; // Ignore no-op weights. if (weight == 0) return; _owner->_weightTable->AddWithWeight(src._index, _index, weight, _owner->_weightTable->GetScalarAccumulator()); } void StencilBuilder::Index::AddWithWeight(Stencil const& src, float weight) { if(weight == 0.0f) { return; } int srcSize = *src.GetSizePtr(); Vtr::Index const * srcIndices = src.GetVertexIndices(); float const * srcWeights = src.GetWeights(); for (int i = 0; i < srcSize; ++i) { float w = srcWeights[i]; if (w == 0.0f) { continue; } Vtr::Index srcIndex = srcIndices[i]; float wgt = weight * w; _owner->_weightTable->AddWithWeight(srcIndex, _index, wgt, _owner->_weightTable->GetScalarAccumulator()); } } void StencilBuilder::Index::AddWithWeight(Stencil const& src, float weight, float du, float dv) { if(weight == 0.0f and du == 0.0f and dv == 0.0f) { return; } int srcSize = *src.GetSizePtr(); Vtr::Index const * srcIndices = src.GetVertexIndices(); float const * srcWeights = src.GetWeights(); for (int i = 0; i < srcSize; ++i) { float w = srcWeights[i]; if (w == 0.0f) { continue; } Vtr::Index srcIndex = srcIndices[i]; PointDerivWeight wgt = PointDerivWeight(weight, du, dv) * w; _owner->_weightTable->AddWithWeight(srcIndex, _index, wgt, _owner->_weightTable->GetPointDerivAccumulator()); } } void StencilBuilder::Index::AddVaryingWithWeight(Index const &src, float weight) { if (not _owner->_isVarying) return; // Ignore no-op weights. if (weight == 0) return; _owner->_weightTable->AddWithWeight(src._index, _index, weight, _owner->_weightTable->GetScalarAccumulator()); } void StencilBuilder::Index::AddFaceVaryingWithWeight(Index const &, float) { // Not supported. } } // end namespace Internal } // end namespace Far } // end namespace OPENSUBDIV_VERSION } // end namespace OpenSubdiv