From f3059723771b90337b126a87a58b1ada981a21b1 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 16 Oct 2019 14:59:18 +0200 Subject: OpenSubdiv: Initial implementation of batched evaluation The idea is to give multiple coordinates to evaluator and evaluate them all at once, avoiding any possible overhead. --- intern/opensubdiv/internal/opensubdiv_evaluator.cc | 13 +++ .../internal/opensubdiv_evaluator_internal.cc | 112 +++++++++++++++++++++ .../internal/opensubdiv_evaluator_internal.h | 13 +++ intern/opensubdiv/opensubdiv_capi_type.h | 7 ++ intern/opensubdiv/opensubdiv_evaluator_capi.h | 14 +++ 5 files changed, 159 insertions(+) (limited to 'intern/opensubdiv') diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator.cc b/intern/opensubdiv/internal/opensubdiv_evaluator.cc index 2500691885c..4f5a1db82ca 100644 --- a/intern/opensubdiv/internal/opensubdiv_evaluator.cc +++ b/intern/opensubdiv/internal/opensubdiv_evaluator.cc @@ -102,6 +102,17 @@ void evaluateLimit(OpenSubdiv_Evaluator *evaluator, evaluator->internal->eval_output->evaluateLimit(ptex_face_index, face_u, face_v, P, dPdu, dPdv); } +void evaluatePatchesLimit(OpenSubdiv_Evaluator *evaluator, + const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv) +{ + evaluator->internal->eval_output->evaluatePatchesLimit( + patch_coords, num_patch_coords, P, dPdu, dPdv); +} + void evaluateVarying(OpenSubdiv_Evaluator *evaluator, const int ptex_face_index, float face_u, @@ -137,6 +148,8 @@ void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator) evaluator->evaluateLimit = evaluateLimit; evaluator->evaluateVarying = evaluateVarying; evaluator->evaluateFaceVarying = evaluateFaceVarying; + + evaluator->evaluatePatchesLimit = evaluatePatchesLimit; } } // namespace diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc index acd8472b5f1..c5dd4509976 100644 --- a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc +++ b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc @@ -58,6 +58,88 @@ namespace opensubdiv_capi { namespace { +// Array implementation which stores small data on stack (or, rather, in the class itself). +template class StackOrHeapArray { + public: + StackOrHeapArray() + : num_elements_(0), heap_elements_(NULL), num_heap_elements_(0), effective_elements_(NULL) + { + } + + explicit StackOrHeapArray(int size) : StackOrHeapArray() + { + resize(size); + } + + ~StackOrHeapArray() + { + delete[] heap_elements_; + } + + int size() const + { + return num_elements_; + }; + + T *data() + { + return effective_elements_; + } + + void resize(int num_elements) + { + const int old_num_elements = num_elements_; + num_elements_ = num_elements; + // Early output if allcoation size did not change, or allocation size is smaller. + // We never re-allocate, sacrificing some memory over performance. + if (old_num_elements >= num_elements) { + return; + } + // Simple case: no previously allocated buffer, can simply do one allocation. + if (effective_elements_ == NULL) { + effective_elements_ = allocate(num_elements); + return; + } + // Make new allocation, and copy elements if needed. + T *old_buffer = effective_elements_; + effective_elements_ = allocate(num_elements); + if (old_buffer != effective_elements_) { + memcpy(effective_elements_, old_buffer, sizeof(T) * min(old_num_elements, num_elements)); + } + if (old_buffer != stack_elements_) { + delete[] old_buffer; + } + } + + protected: + T *allocate(int num_elements) + { + if (num_elements < kNumMaxElementsOnStack) { + return stack_elements_; + } + heap_elements_ = new T[num_elements]; + return heap_elements_; + } + + // Number of elements in the buffer. + int num_elements_; + + // Elements which are allocated on a stack (or, rather, in the same allocation as the buffer + // itself). + // Is used as long as buffer is smaller than kNumMaxElementsOnStack. + T stack_elements_[kNumMaxElementsOnStack]; + + // Heap storage for buffer larger than kNumMaxElementsOnStack. + T *heap_elements_; + int num_heap_elements_; + + // Depending on the current buffer size points to rither stack_elements_ or heap_elements_. + T *effective_elements_; +}; + +// 32 is a number of inner vertices along the patch size at subdivision level 6. +typedef StackOrHeapArray StackOrHeapPatchCoordArray; + // Buffer which implements API required by OpenSubdiv and uses an existing memory as an underlying // storage. template class RawDataWrapperBuffer { @@ -441,6 +523,19 @@ class VolatileEvalOutput { DEVICE_CONTEXT *device_context_; }; +void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + const OpenSubdiv::Far::PatchMap *patch_map, + StackOrHeapPatchCoordArray *array) +{ + array->resize(num_patch_coords); + for (int i = 0; i < num_patch_coords; ++i) { + const PatchTable::PatchHandle *handle = patch_map->FindPatch( + patch_coords[i].ptex_face, patch_coords[i].u, patch_coords[i].v); + (array->data())[i] = PatchCoord(*handle, patch_coords[i].u, patch_coords[i].v); + } +} + } // namespace // Note: Define as a class instead of typedcef to make it possible @@ -620,6 +715,23 @@ void CpuEvalOutputAPI::evaluateFaceVarying(const int face_varying_channel, implementation_->evalPatchesFaceVarying(face_varying_channel, &patch_coord, 1, face_varying); } +void CpuEvalOutputAPI::evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv) +{ + StackOrHeapPatchCoordArray patch_coords_array; + convertPatchCoordsToArray(patch_coords, num_patch_coords, patch_map_, &patch_coords_array); + if (dPdu != NULL || dPdv != NULL) { + implementation_->evalPatchesWithDerivatives( + patch_coords_array.data(), num_patch_coords, P, dPdu, dPdv); + } + else { + implementation_->evalPatches(patch_coords_array.data(), num_patch_coords, P); + } +} + } // namespace opensubdiv_capi OpenSubdiv_EvaluatorInternal::OpenSubdiv_EvaluatorInternal() diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h index 7c963227d17..392633944c6 100644 --- a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h +++ b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h @@ -26,6 +26,7 @@ #include #include +struct OpenSubdiv_PatchCoord; struct OpenSubdiv_TopologyRefiner; namespace opensubdiv_capi { @@ -114,6 +115,18 @@ class CpuEvalOutputAPI { float face_v, float face_varying[2]); + // Batched evaluation of multiple input coordinates. + + // Evaluate given ptex face at given bilinear coordinate. + // If derivatives are NULL, they will not be evaluated. + // + // NOTE: Output arrays must point to a memory of size float[3]*num_patch_coords. + void evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv); + protected: CpuEvalOutput *implementation_; OpenSubdiv::Far::PatchMap *patch_map_; diff --git a/intern/opensubdiv/opensubdiv_capi_type.h b/intern/opensubdiv/opensubdiv_capi_type.h index 35eeb71dede..e759c5f43b0 100644 --- a/intern/opensubdiv/opensubdiv_capi_type.h +++ b/intern/opensubdiv/opensubdiv_capi_type.h @@ -58,6 +58,13 @@ typedef enum OpenSubdiv_FVarLinearInterpolation { OSD_FVAR_LINEAR_INTERPOLATION_ALL, } OpenSubdiv_FVarLinearInterpolation; +typedef struct OpenSubdiv_PatchCoord { + int ptex_face; + + // Parametric location on patch. + float u, v; +} OpenSubdiv_PatchCoord; + #ifdef __cplusplus } #endif diff --git a/intern/opensubdiv/opensubdiv_evaluator_capi.h b/intern/opensubdiv/opensubdiv_evaluator_capi.h index ceb0c58feba..1572d01b851 100644 --- a/intern/opensubdiv/opensubdiv_evaluator_capi.h +++ b/intern/opensubdiv/opensubdiv_evaluator_capi.h @@ -24,6 +24,7 @@ extern "C" { #endif struct OpenSubdiv_EvaluatorInternal; +struct OpenSubdiv_PatchCoord; struct OpenSubdiv_TopologyRefiner; typedef struct OpenSubdiv_Evaluator { @@ -108,6 +109,19 @@ typedef struct OpenSubdiv_Evaluator { float face_v, float face_varying[2]); + // Batched evaluation of multiple input coordinates. + + // Evaluate limit surface. + // If derivatives are NULL, they will not be evaluated. + // + // NOTE: Output arrays must point to a memory of size float[3]*num_patch_coords. + void (*evaluatePatchesLimit)(struct OpenSubdiv_Evaluator *evaluator, + const struct OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv); + // Internal storage for the use in this module only. // // This is where actual OpenSubdiv's evaluator is living. -- cgit v1.2.3