Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/opensubdiv')
-rw-r--r--intern/opensubdiv/CMakeLists.txt11
-rw-r--r--intern/opensubdiv/internal/evaluator/eval_output.cc35
-rw-r--r--intern/opensubdiv/internal/evaluator/eval_output.h582
-rw-r--r--intern/opensubdiv/internal/evaluator/eval_output_cpu.cc23
-rw-r--r--intern/opensubdiv/internal/evaluator/eval_output_cpu.h66
-rw-r--r--intern/opensubdiv/internal/evaluator/eval_output_gpu.cc120
-rw-r--r--intern/opensubdiv/internal/evaluator/eval_output_gpu.h71
-rw-r--r--intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc47
-rw-r--r--intern/opensubdiv/internal/evaluator/evaluator_cache_impl.h38
-rw-r--r--intern/opensubdiv/internal/evaluator/evaluator_capi.cc118
-rw-r--r--intern/opensubdiv/internal/evaluator/evaluator_impl.cc649
-rw-r--r--intern/opensubdiv/internal/evaluator/evaluator_impl.h69
-rw-r--r--intern/opensubdiv/internal/evaluator/patch_map.cc212
-rw-r--r--intern/opensubdiv/internal/evaluator/patch_map.h264
-rw-r--r--intern/opensubdiv/opensubdiv_evaluator_capi.h101
15 files changed, 1900 insertions, 506 deletions
diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt
index bce8a8baa84..f141689791f 100644
--- a/intern/opensubdiv/CMakeLists.txt
+++ b/intern/opensubdiv/CMakeLists.txt
@@ -72,9 +72,20 @@ if(WITH_OPENSUBDIV)
internal/device/device_context_openmp.h
# Evaluator.
+ internal/evaluator/eval_output.cc
+ internal/evaluator/eval_output.h
+ internal/evaluator/eval_output_cpu.cc
+ internal/evaluator/eval_output_cpu.h
+ internal/evaluator/eval_output_gpu.cc
+ internal/evaluator/eval_output_gpu.h
+ internal/evaluator/evaluator_cache_impl.cc
+ internal/evaluator/evaluator_cache_impl.h
internal/evaluator/evaluator_capi.cc
internal/evaluator/evaluator_impl.cc
internal/evaluator/evaluator_impl.h
+ internal/evaluator/patch_map.cc
+ internal/evaluator/patch_map.h
+
# Topology.
internal/topology/mesh_topology.cc
diff --git a/intern/opensubdiv/internal/evaluator/eval_output.cc b/intern/opensubdiv/internal/evaluator/eval_output.cc
new file mode 100644
index 00000000000..58f835ab2e0
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/eval_output.cc
@@ -0,0 +1,35 @@
+// Copyright 2021 Blender Foundation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// Author: Sergey Sharybin
+
+#include "internal/evaluator/eval_output.h"
+
+namespace blender {
+namespace opensubdiv {
+
+bool is_adaptive(CpuPatchTable *patch_table)
+{
+ return patch_table->GetPatchArrayBuffer()[0].GetDescriptor().IsAdaptive();
+}
+
+bool is_adaptive(GLPatchTable *patch_table)
+{
+ return patch_table->GetPatchArrays()[0].GetDescriptor().IsAdaptive();
+}
+
+} // namespace opensubdiv
+} // namespace blender
diff --git a/intern/opensubdiv/internal/evaluator/eval_output.h b/intern/opensubdiv/internal/evaluator/eval_output.h
new file mode 100644
index 00000000000..a55b89001a4
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/eval_output.h
@@ -0,0 +1,582 @@
+// Copyright 2021 Blender Foundation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// Author: Sergey Sharybin
+
+#ifndef OPENSUBDIV_EVAL_OUTPUT_H_
+#define OPENSUBDIV_EVAL_OUTPUT_H_
+
+#include <opensubdiv/osd/cpuPatchTable.h>
+#include <opensubdiv/osd/glPatchTable.h>
+#include <opensubdiv/osd/mesh.h>
+#include <opensubdiv/osd/types.h>
+
+#include "internal/base/type.h"
+#include "internal/evaluator/evaluator_impl.h"
+
+using OpenSubdiv::Far::PatchTable;
+using OpenSubdiv::Far::StencilTable;
+using OpenSubdiv::Osd::BufferDescriptor;
+using OpenSubdiv::Osd::CpuPatchTable;
+using OpenSubdiv::Osd::GLPatchTable;
+using OpenSubdiv::Osd::PatchCoord;
+
+namespace blender {
+namespace opensubdiv {
+
+// Base class for the implementation of the evaluators.
+class EvalOutputAPI::EvalOutput {
+ public:
+ virtual ~EvalOutput() = default;
+
+ virtual void updateData(const float *src, int start_vertex, int num_vertices) = 0;
+
+ virtual void updateVaryingData(const float *src, int start_vertex, int num_vertices) = 0;
+
+ virtual void updateFaceVaryingData(const int face_varying_channel,
+ const float *src,
+ int start_vertex,
+ int num_vertices) = 0;
+
+ virtual void refine() = 0;
+
+ // NOTE: P must point to a memory of at least float[3]*num_patch_coords.
+ virtual void evalPatches(const PatchCoord *patch_coord,
+ const int num_patch_coords,
+ float *P) = 0;
+
+ // NOTE: P, dPdu, dPdv must point to a memory of at least float[3]*num_patch_coords.
+ virtual void evalPatchesWithDerivatives(const PatchCoord *patch_coord,
+ const int num_patch_coords,
+ float *P,
+ float *dPdu,
+ float *dPdv) = 0;
+
+ // NOTE: varying must point to a memory of at least float[3]*num_patch_coords.
+ virtual void evalPatchesVarying(const PatchCoord *patch_coord,
+ const int num_patch_coords,
+ float *varying) = 0;
+
+ virtual void evalPatchesFaceVarying(const int face_varying_channel,
+ const PatchCoord *patch_coord,
+ const int num_patch_coords,
+ float face_varying[2]) = 0;
+
+ // The following interfaces are dependant on the actual evaluator type (CPU, OpenGL, etc.) which
+ // have slightly different APIs to access patch arrays, as well as different types for their
+ // data structure. They need to be overridden in the specific instances of the EvalOutput derived
+ // classes if needed, while the interfaces above are overriden through VolatileEvalOutput.
+
+ virtual void fillPatchArraysBuffer(OpenSubdiv_Buffer * /*patch_arrays_buffer*/)
+ {
+ }
+
+ virtual void wrapPatchIndexBuffer(OpenSubdiv_Buffer * /*patch_index_buffer*/)
+ {
+ }
+
+ virtual void wrapPatchParamBuffer(OpenSubdiv_Buffer * /*patch_param_buffer*/)
+ {
+ }
+
+ virtual void wrapSrcBuffer(OpenSubdiv_Buffer * /*src_buffer*/)
+ {
+ }
+
+ virtual void fillFVarPatchArraysBuffer(const int /*face_varying_channel*/,
+ OpenSubdiv_Buffer * /*patch_arrays_buffer*/)
+ {
+ }
+
+ virtual void wrapFVarPatchIndexBuffer(const int /*face_varying_channel*/,
+ OpenSubdiv_Buffer * /*patch_index_buffer*/)
+ {
+ }
+
+ virtual void wrapFVarPatchParamBuffer(const int /*face_varying_channel*/,
+ OpenSubdiv_Buffer * /*patch_param_buffer*/)
+ {
+ }
+
+ virtual void wrapFVarSrcBuffer(const int /*face_varying_channel*/,
+ OpenSubdiv_Buffer * /*src_buffer*/)
+ {
+ }
+};
+
+namespace {
+
+// Buffer which implements API required by OpenSubdiv and uses an existing memory as an underlying
+// storage.
+template<typename T> class RawDataWrapperBuffer {
+ public:
+ RawDataWrapperBuffer(T *data) : data_(data)
+ {
+ }
+
+ T *BindCpuBuffer()
+ {
+ return data_;
+ }
+
+ int BindVBO()
+ {
+ return 0;
+ }
+
+ // TODO(sergey): Support UpdateData().
+
+ protected:
+ T *data_;
+};
+
+template<typename T> class RawDataWrapperVertexBuffer : public RawDataWrapperBuffer<T> {
+ public:
+ RawDataWrapperVertexBuffer(T *data, int num_vertices)
+ : RawDataWrapperBuffer<T>(data), num_vertices_(num_vertices)
+ {
+ }
+
+ int GetNumVertices()
+ {
+ return num_vertices_;
+ }
+
+ protected:
+ int num_vertices_;
+};
+
+class ConstPatchCoordWrapperBuffer : public RawDataWrapperVertexBuffer<const PatchCoord> {
+ public:
+ ConstPatchCoordWrapperBuffer(const PatchCoord *data, int num_vertices)
+ : RawDataWrapperVertexBuffer(data, num_vertices)
+ {
+ }
+};
+} // namespace
+
+// Discriminators used in FaceVaryingVolatileEval in order to detect whether we are using adaptive
+// patches as the CPU and OpenGL PatchTable have different APIs.
+bool is_adaptive(CpuPatchTable *patch_table);
+bool is_adaptive(GLPatchTable *patch_table);
+
+template<typename EVAL_VERTEX_BUFFER,
+ typename STENCIL_TABLE,
+ typename PATCH_TABLE,
+ typename EVALUATOR,
+ typename DEVICE_CONTEXT = void>
+class FaceVaryingVolatileEval {
+ public:
+ typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache;
+
+ FaceVaryingVolatileEval(int face_varying_channel,
+ const StencilTable *face_varying_stencils,
+ int face_varying_width,
+ PATCH_TABLE *patch_table,
+ EvaluatorCache *evaluator_cache = NULL,
+ DEVICE_CONTEXT *device_context = NULL)
+ : face_varying_channel_(face_varying_channel),
+ src_face_varying_desc_(0, face_varying_width, face_varying_width),
+ patch_table_(patch_table),
+ evaluator_cache_(evaluator_cache),
+ device_context_(device_context)
+ {
+ using OpenSubdiv::Osd::convertToCompatibleStencilTable;
+ num_coarse_face_varying_vertices_ = face_varying_stencils->GetNumControlVertices();
+ const int num_total_face_varying_vertices = face_varying_stencils->GetNumControlVertices() +
+ face_varying_stencils->GetNumStencils();
+ src_face_varying_data_ = EVAL_VERTEX_BUFFER::Create(
+ 2, num_total_face_varying_vertices, device_context);
+ face_varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(face_varying_stencils,
+ device_context_);
+ }
+
+ ~FaceVaryingVolatileEval()
+ {
+ delete src_face_varying_data_;
+ delete face_varying_stencils_;
+ }
+
+ void updateData(const float *src, int start_vertex, int num_vertices)
+ {
+ src_face_varying_data_->UpdateData(src, start_vertex, num_vertices, device_context_);
+ }
+
+ void refine()
+ {
+ BufferDescriptor dst_face_varying_desc = src_face_varying_desc_;
+ dst_face_varying_desc.offset += num_coarse_face_varying_vertices_ *
+ src_face_varying_desc_.stride;
+ const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
+ evaluator_cache_, src_face_varying_desc_, dst_face_varying_desc, device_context_);
+ // in and out points to same buffer so output is put directly after coarse vertices, needed in
+ // adaptive mode
+ EVALUATOR::EvalStencils(src_face_varying_data_,
+ src_face_varying_desc_,
+ src_face_varying_data_,
+ dst_face_varying_desc,
+ face_varying_stencils_,
+ eval_instance,
+ device_context_);
+ }
+
+ // NOTE: face_varying must point to a memory of at least float[2]*num_patch_coords.
+ void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *face_varying)
+ {
+ RawDataWrapperBuffer<float> face_varying_data(face_varying);
+ BufferDescriptor face_varying_desc(0, 2, 2);
+ ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
+ const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
+ evaluator_cache_, src_face_varying_desc_, face_varying_desc, device_context_);
+
+ BufferDescriptor src_desc = get_src_varying_desc();
+
+ EVALUATOR::EvalPatchesFaceVarying(src_face_varying_data_,
+ src_desc,
+ &face_varying_data,
+ face_varying_desc,
+ patch_coord_buffer.GetNumVertices(),
+ &patch_coord_buffer,
+ patch_table_,
+ face_varying_channel_,
+ eval_instance,
+ device_context_);
+ }
+
+ EVAL_VERTEX_BUFFER *getSrcBuffer() const
+ {
+ return src_face_varying_data_;
+ }
+
+ int getFVarSrcBufferOffset() const
+ {
+ BufferDescriptor src_desc = get_src_varying_desc();
+ return src_desc.offset;
+ }
+
+ PATCH_TABLE *getPatchTable() const
+ {
+ return patch_table_;
+ }
+
+ private:
+ BufferDescriptor get_src_varying_desc() const
+ {
+ // src_face_varying_data_ always contains coarse vertices at the beginning.
+ // In adaptive mode they are followed by number of blocks for intermediate
+ // subdivision levels, and this is what OSD expects in this mode.
+ // In non-adaptive mode (generateIntermediateLevels == false),
+ // they are followed by max subdivision level, but they break interpolation as OSD
+ // expects only one subd level in this buffer.
+ // So in non-adaptive mode we put offset into buffer descriptor to skip over coarse vertices.
+ BufferDescriptor src_desc = src_face_varying_desc_;
+ if (!is_adaptive(patch_table_)) {
+ src_desc.offset += num_coarse_face_varying_vertices_ * src_face_varying_desc_.stride;
+ }
+ return src_desc;
+ }
+
+ protected:
+ int face_varying_channel_;
+
+ BufferDescriptor src_face_varying_desc_;
+
+ int num_coarse_face_varying_vertices_;
+ EVAL_VERTEX_BUFFER *src_face_varying_data_;
+ const STENCIL_TABLE *face_varying_stencils_;
+
+ // NOTE: We reference this, do not own it.
+ PATCH_TABLE *patch_table_;
+
+ EvaluatorCache *evaluator_cache_;
+ DEVICE_CONTEXT *device_context_;
+};
+
+// Volatile evaluator which can be used from threads.
+//
+// TODO(sergey): Make it possible to evaluate coordinates in chunks.
+// TODO(sergey): Make it possible to evaluate multiple face varying layers.
+// (or maybe, it's cheap to create new evaluator for existing
+// topology to evaluate all needed face varying layers?)
+template<typename SRC_VERTEX_BUFFER,
+ typename EVAL_VERTEX_BUFFER,
+ typename STENCIL_TABLE,
+ typename PATCH_TABLE,
+ typename EVALUATOR,
+ typename DEVICE_CONTEXT = void>
+class VolatileEvalOutput : public EvalOutputAPI::EvalOutput {
+ public:
+ typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache;
+ typedef FaceVaryingVolatileEval<EVAL_VERTEX_BUFFER,
+ STENCIL_TABLE,
+ PATCH_TABLE,
+ EVALUATOR,
+ DEVICE_CONTEXT>
+ FaceVaryingEval;
+
+ VolatileEvalOutput(const StencilTable *vertex_stencils,
+ const StencilTable *varying_stencils,
+ const vector<const StencilTable *> &all_face_varying_stencils,
+ const int face_varying_width,
+ const PatchTable *patch_table,
+ EvaluatorCache *evaluator_cache = NULL,
+ DEVICE_CONTEXT *device_context = NULL)
+ : src_desc_(0, 3, 3),
+ src_varying_desc_(0, 3, 3),
+ face_varying_width_(face_varying_width),
+ evaluator_cache_(evaluator_cache),
+ device_context_(device_context)
+ {
+ // Total number of vertices = coarse points + refined points + local points.
+ int num_total_vertices = vertex_stencils->GetNumControlVertices() +
+ vertex_stencils->GetNumStencils();
+ num_coarse_vertices_ = vertex_stencils->GetNumControlVertices();
+ using OpenSubdiv::Osd::convertToCompatibleStencilTable;
+ src_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_);
+ src_varying_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_);
+ patch_table_ = PATCH_TABLE::Create(patch_table, device_context_);
+ vertex_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(vertex_stencils,
+ device_context_);
+ varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(varying_stencils,
+ device_context_);
+ // Create evaluators for every face varying channel.
+ face_varying_evaluators.reserve(all_face_varying_stencils.size());
+ int face_varying_channel = 0;
+ for (const StencilTable *face_varying_stencils : all_face_varying_stencils) {
+ face_varying_evaluators.push_back(new FaceVaryingEval(face_varying_channel,
+ face_varying_stencils,
+ face_varying_width,
+ patch_table_,
+ evaluator_cache_,
+ device_context_));
+ ++face_varying_channel;
+ }
+ }
+
+ ~VolatileEvalOutput() override
+ {
+ delete src_data_;
+ delete src_varying_data_;
+ delete patch_table_;
+ delete vertex_stencils_;
+ delete varying_stencils_;
+ for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) {
+ delete face_varying_evaluator;
+ }
+ }
+
+ // TODO(sergey): Implement binding API.
+
+ void updateData(const float *src, int start_vertex, int num_vertices) override
+ {
+ src_data_->UpdateData(src, start_vertex, num_vertices, device_context_);
+ }
+
+ void updateVaryingData(const float *src, int start_vertex, int num_vertices) override
+ {
+ src_varying_data_->UpdateData(src, start_vertex, num_vertices, device_context_);
+ }
+
+ void updateFaceVaryingData(const int face_varying_channel,
+ const float *src,
+ int start_vertex,
+ int num_vertices) override
+ {
+ assert(face_varying_channel >= 0);
+ assert(face_varying_channel < face_varying_evaluators.size());
+ face_varying_evaluators[face_varying_channel]->updateData(src, start_vertex, num_vertices);
+ }
+
+ bool hasVaryingData() const
+ {
+ // return varying_stencils_ != NULL;
+ // TODO(sergey): Check this based on actual topology.
+ return false;
+ }
+
+ bool hasFaceVaryingData() const
+ {
+ return face_varying_evaluators.size() != 0;
+ }
+
+ void refine() override
+ {
+ // Evaluate vertex positions.
+ BufferDescriptor dst_desc = src_desc_;
+ dst_desc.offset += num_coarse_vertices_ * src_desc_.stride;
+ const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
+ evaluator_cache_, src_desc_, dst_desc, device_context_);
+ EVALUATOR::EvalStencils(src_data_,
+ src_desc_,
+ src_data_,
+ dst_desc,
+ vertex_stencils_,
+ eval_instance,
+ device_context_);
+ // Evaluate varying data.
+ if (hasVaryingData()) {
+ BufferDescriptor dst_varying_desc = src_varying_desc_;
+ dst_varying_desc.offset += num_coarse_vertices_ * src_varying_desc_.stride;
+ eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
+ evaluator_cache_, src_varying_desc_, dst_varying_desc, device_context_);
+ EVALUATOR::EvalStencils(src_varying_data_,
+ src_varying_desc_,
+ src_varying_data_,
+ dst_varying_desc,
+ varying_stencils_,
+ eval_instance,
+ device_context_);
+ }
+ // Evaluate face-varying data.
+ if (hasFaceVaryingData()) {
+ for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) {
+ face_varying_evaluator->refine();
+ }
+ }
+ }
+
+ // NOTE: P must point to a memory of at least float[3]*num_patch_coords.
+ void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *P) override
+ {
+ RawDataWrapperBuffer<float> P_data(P);
+ // TODO(sergey): Support interleaved vertex-varying data.
+ BufferDescriptor P_desc(0, 3, 3);
+ ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
+ const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
+ evaluator_cache_, src_desc_, P_desc, device_context_);
+ EVALUATOR::EvalPatches(src_data_,
+ src_desc_,
+ &P_data,
+ P_desc,
+ patch_coord_buffer.GetNumVertices(),
+ &patch_coord_buffer,
+ patch_table_,
+ eval_instance,
+ device_context_);
+ }
+
+ // NOTE: P, dPdu, dPdv must point to a memory of at least float[3]*num_patch_coords.
+ void evalPatchesWithDerivatives(const PatchCoord *patch_coord,
+ const int num_patch_coords,
+ float *P,
+ float *dPdu,
+ float *dPdv) override
+ {
+ assert(dPdu);
+ assert(dPdv);
+ RawDataWrapperBuffer<float> P_data(P);
+ RawDataWrapperBuffer<float> dPdu_data(dPdu), dPdv_data(dPdv);
+ // TODO(sergey): Support interleaved vertex-varying data.
+ BufferDescriptor P_desc(0, 3, 3);
+ BufferDescriptor dpDu_desc(0, 3, 3), pPdv_desc(0, 3, 3);
+ ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
+ const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
+ evaluator_cache_, src_desc_, P_desc, dpDu_desc, pPdv_desc, device_context_);
+ EVALUATOR::EvalPatches(src_data_,
+ src_desc_,
+ &P_data,
+ P_desc,
+ &dPdu_data,
+ dpDu_desc,
+ &dPdv_data,
+ pPdv_desc,
+ patch_coord_buffer.GetNumVertices(),
+ &patch_coord_buffer,
+ patch_table_,
+ eval_instance,
+ device_context_);
+ }
+
+ // NOTE: varying must point to a memory of at least float[3]*num_patch_coords.
+ void evalPatchesVarying(const PatchCoord *patch_coord,
+ const int num_patch_coords,
+ float *varying) override
+ {
+ RawDataWrapperBuffer<float> varying_data(varying);
+ BufferDescriptor varying_desc(3, 3, 6);
+ ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
+ const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
+ evaluator_cache_, src_varying_desc_, varying_desc, device_context_);
+ EVALUATOR::EvalPatchesVarying(src_varying_data_,
+ src_varying_desc_,
+ &varying_data,
+ varying_desc,
+ patch_coord_buffer.GetNumVertices(),
+ &patch_coord_buffer,
+ patch_table_,
+ eval_instance,
+ device_context_);
+ }
+
+ void evalPatchesFaceVarying(const int face_varying_channel,
+ const PatchCoord *patch_coord,
+ const int num_patch_coords,
+ float face_varying[2]) override
+ {
+ assert(face_varying_channel >= 0);
+ assert(face_varying_channel < face_varying_evaluators.size());
+ face_varying_evaluators[face_varying_channel]->evalPatches(
+ patch_coord, num_patch_coords, face_varying);
+ }
+
+ SRC_VERTEX_BUFFER *getSrcBuffer() const
+ {
+ return src_data_;
+ }
+
+ PATCH_TABLE *getPatchTable() const
+ {
+ return patch_table_;
+ }
+
+ SRC_VERTEX_BUFFER *getFVarSrcBuffer(const int face_varying_channel) const
+ {
+ return face_varying_evaluators[face_varying_channel]->getSrcBuffer();
+ }
+
+ int getFVarSrcBufferOffset(const int face_varying_channel) const
+ {
+ return face_varying_evaluators[face_varying_channel]->getFVarSrcBufferOffset();
+ }
+
+ PATCH_TABLE *getFVarPatchTable(const int face_varying_channel) const
+ {
+ return face_varying_evaluators[face_varying_channel]->getPatchTable();
+ }
+
+ private:
+ SRC_VERTEX_BUFFER *src_data_;
+ SRC_VERTEX_BUFFER *src_varying_data_;
+ PATCH_TABLE *patch_table_;
+ BufferDescriptor src_desc_;
+ BufferDescriptor src_varying_desc_;
+
+ int num_coarse_vertices_;
+
+ const STENCIL_TABLE *vertex_stencils_;
+ const STENCIL_TABLE *varying_stencils_;
+
+ int face_varying_width_;
+ vector<FaceVaryingEval *> face_varying_evaluators;
+
+ EvaluatorCache *evaluator_cache_;
+ DEVICE_CONTEXT *device_context_;
+};
+
+} // namespace opensubdiv
+} // namespace blender
+
+#endif // OPENSUBDIV_EVAL_OUTPUT_H_
diff --git a/intern/opensubdiv/internal/evaluator/eval_output_cpu.cc b/intern/opensubdiv/internal/evaluator/eval_output_cpu.cc
new file mode 100644
index 00000000000..02a41cb1580
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/eval_output_cpu.cc
@@ -0,0 +1,23 @@
+// Copyright 2021 Blender Foundation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// Author: Sergey Sharybin
+
+namespace blender {
+namespace opensubdiv {
+
+}
+} // namespace blender
diff --git a/intern/opensubdiv/internal/evaluator/eval_output_cpu.h b/intern/opensubdiv/internal/evaluator/eval_output_cpu.h
new file mode 100644
index 00000000000..58bae7a322e
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/eval_output_cpu.h
@@ -0,0 +1,66 @@
+// Copyright 2021 Blender Foundation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// Author: Sergey Sharybin
+
+#ifndef OPENSUBDIV_EVAL_OUTPUT_CPU_H_
+#define OPENSUBDIV_EVAL_OUTPUT_CPU_H_
+
+#include "internal/evaluator/eval_output.h"
+
+#include <opensubdiv/osd/cpuEvaluator.h>
+#include <opensubdiv/osd/cpuPatchTable.h>
+#include <opensubdiv/osd/cpuVertexBuffer.h>
+
+using OpenSubdiv::Far::StencilTable;
+using OpenSubdiv::Osd::CpuEvaluator;
+using OpenSubdiv::Osd::CpuVertexBuffer;
+
+namespace blender {
+namespace opensubdiv {
+
+// Note: Define as a class instead of typedef to make it possible
+// to have anonymous class in opensubdiv_evaluator_internal.h
+class CpuEvalOutput : public VolatileEvalOutput<CpuVertexBuffer,
+ CpuVertexBuffer,
+ StencilTable,
+ CpuPatchTable,
+ CpuEvaluator> {
+ public:
+ CpuEvalOutput(const StencilTable *vertex_stencils,
+ const StencilTable *varying_stencils,
+ const vector<const StencilTable *> &all_face_varying_stencils,
+ const int face_varying_width,
+ const PatchTable *patch_table,
+ EvaluatorCache *evaluator_cache = NULL)
+ : VolatileEvalOutput<CpuVertexBuffer,
+ CpuVertexBuffer,
+ StencilTable,
+ CpuPatchTable,
+ CpuEvaluator>(vertex_stencils,
+ varying_stencils,
+ all_face_varying_stencils,
+ face_varying_width,
+ patch_table,
+ evaluator_cache)
+ {
+ }
+};
+
+} // namespace opensubdiv
+} // namespace blender
+
+#endif // OPENSUBDIV_EVAL_OUTPUT_CPU_H_
diff --git a/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc b/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc
new file mode 100644
index 00000000000..b352ed2c014
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc
@@ -0,0 +1,120 @@
+// Copyright 2021 Blender Foundation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// Author: Sergey Sharybin
+
+#include "internal/evaluator/eval_output_gpu.h"
+
+#include "opensubdiv_evaluator_capi.h"
+
+using OpenSubdiv::Osd::PatchArray;
+using OpenSubdiv::Osd::PatchArrayVector;
+
+namespace blender {
+namespace opensubdiv {
+
+namespace {
+
+static void buildPatchArraysBufferFromVector(const PatchArrayVector &patch_arrays,
+ OpenSubdiv_Buffer *patch_arrays_buffer)
+{
+ const size_t patch_array_size = sizeof(PatchArray);
+ const size_t patch_array_byte_site = patch_array_size * patch_arrays.size();
+ patch_arrays_buffer->device_alloc(patch_arrays_buffer, patch_arrays.size());
+ patch_arrays_buffer->bind_gpu(patch_arrays_buffer);
+ patch_arrays_buffer->device_update(
+ patch_arrays_buffer, 0, patch_array_byte_site, &patch_arrays[0]);
+}
+
+} // namespace
+
+GpuEvalOutput::GpuEvalOutput(const StencilTable *vertex_stencils,
+ const StencilTable *varying_stencils,
+ const vector<const StencilTable *> &all_face_varying_stencils,
+ const int face_varying_width,
+ const PatchTable *patch_table,
+ VolatileEvalOutput::EvaluatorCache *evaluator_cache)
+ : VolatileEvalOutput<GLVertexBuffer,
+ GLVertexBuffer,
+ GLStencilTableSSBO,
+ GLPatchTable,
+ GLComputeEvaluator>(vertex_stencils,
+ varying_stencils,
+ all_face_varying_stencils,
+ face_varying_width,
+ patch_table,
+ evaluator_cache)
+{
+}
+
+void GpuEvalOutput::fillPatchArraysBuffer(OpenSubdiv_Buffer *patch_arrays_buffer)
+{
+ GLPatchTable *patch_table = getPatchTable();
+ buildPatchArraysBufferFromVector(patch_table->GetPatchArrays(), patch_arrays_buffer);
+}
+
+void GpuEvalOutput::wrapPatchIndexBuffer(OpenSubdiv_Buffer *patch_index_buffer)
+{
+ GLPatchTable *patch_table = getPatchTable();
+ patch_index_buffer->wrap_device_handle(patch_index_buffer, patch_table->GetPatchIndexBuffer());
+}
+
+void GpuEvalOutput::wrapPatchParamBuffer(OpenSubdiv_Buffer *patch_param_buffer)
+{
+ GLPatchTable *patch_table = getPatchTable();
+ patch_param_buffer->wrap_device_handle(patch_param_buffer, patch_table->GetPatchParamBuffer());
+}
+
+void GpuEvalOutput::wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer)
+{
+ GLVertexBuffer *vertex_buffer = getSrcBuffer();
+ src_buffer->wrap_device_handle(src_buffer, vertex_buffer->BindVBO());
+}
+
+void GpuEvalOutput::fillFVarPatchArraysBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_arrays_buffer)
+{
+ GLPatchTable *patch_table = getFVarPatchTable(face_varying_channel);
+ buildPatchArraysBufferFromVector(patch_table->GetFVarPatchArrays(face_varying_channel),
+ patch_arrays_buffer);
+}
+
+void GpuEvalOutput::wrapFVarPatchIndexBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_index_buffer)
+{
+ GLPatchTable *patch_table = getFVarPatchTable(face_varying_channel);
+ patch_index_buffer->wrap_device_handle(
+ patch_index_buffer, patch_table->GetFVarPatchIndexBuffer(face_varying_channel));
+}
+
+void GpuEvalOutput::wrapFVarPatchParamBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_param_buffer)
+{
+ GLPatchTable *patch_table = getFVarPatchTable(face_varying_channel);
+ patch_param_buffer->wrap_device_handle(
+ patch_param_buffer, patch_table->GetFVarPatchParamBuffer(face_varying_channel));
+}
+
+void GpuEvalOutput::wrapFVarSrcBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *src_buffer)
+{
+ GLVertexBuffer *vertex_buffer = getFVarSrcBuffer(face_varying_channel);
+ src_buffer->buffer_offset = getFVarSrcBufferOffset(face_varying_channel);
+ src_buffer->wrap_device_handle(src_buffer, vertex_buffer->BindVBO());
+}
+
+} // namespace opensubdiv
+} // namespace blender
diff --git a/intern/opensubdiv/internal/evaluator/eval_output_gpu.h b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h
new file mode 100644
index 00000000000..783efd484aa
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h
@@ -0,0 +1,71 @@
+// Copyright 2021 Blender Foundation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// Author: Sergey Sharybin
+
+#ifndef OPENSUBDIV_EVAL_OUTPUT_GPU_H_
+#define OPENSUBDIV_EVAL_OUTPUT_GPU_H_
+
+#include "internal/evaluator/eval_output.h"
+
+#include <opensubdiv/osd/glComputeEvaluator.h>
+#include <opensubdiv/osd/glPatchTable.h>
+#include <opensubdiv/osd/glVertexBuffer.h>
+
+using OpenSubdiv::Osd::GLComputeEvaluator;
+using OpenSubdiv::Osd::GLStencilTableSSBO;
+using OpenSubdiv::Osd::GLVertexBuffer;
+
+namespace blender {
+namespace opensubdiv {
+
+class GpuEvalOutput : public VolatileEvalOutput<GLVertexBuffer,
+ GLVertexBuffer,
+ GLStencilTableSSBO,
+ GLPatchTable,
+ GLComputeEvaluator> {
+ public:
+ GpuEvalOutput(const StencilTable *vertex_stencils,
+ const StencilTable *varying_stencils,
+ const vector<const StencilTable *> &all_face_varying_stencils,
+ const int face_varying_width,
+ const PatchTable *patch_table,
+ EvaluatorCache *evaluator_cache = NULL);
+
+ void fillPatchArraysBuffer(OpenSubdiv_Buffer *patch_arrays_buffer) override;
+
+ void wrapPatchIndexBuffer(OpenSubdiv_Buffer *patch_index_buffer) override;
+
+ void wrapPatchParamBuffer(OpenSubdiv_Buffer *patch_param_buffer) override;
+
+ void wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer) override;
+
+ void fillFVarPatchArraysBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_arrays_buffer) override;
+
+ void wrapFVarPatchIndexBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_index_buffer) override;
+
+ void wrapFVarPatchParamBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_param_buffer) override;
+
+ void wrapFVarSrcBuffer(const int face_varying_channel, OpenSubdiv_Buffer *src_buffer) override;
+};
+
+} // namespace opensubdiv
+} // namespace blender
+
+#endif // OPENSUBDIV_EVAL_OUTPUT_GPU_H_
diff --git a/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc
new file mode 100644
index 00000000000..2fffcefa460
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc
@@ -0,0 +1,47 @@
+// Copyright 2021 Blender Foundation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+#include "internal/evaluator/evaluator_cache_impl.h"
+
+#include "internal/evaluator/eval_output_gpu.h"
+
+OpenSubdiv_EvaluatorCacheImpl::OpenSubdiv_EvaluatorCacheImpl()
+{
+}
+
+OpenSubdiv_EvaluatorCacheImpl::~OpenSubdiv_EvaluatorCacheImpl()
+{
+ delete static_cast<blender::opensubdiv::GpuEvalOutput::EvaluatorCache *>(eval_cache);
+}
+
+OpenSubdiv_EvaluatorCacheImpl *openSubdiv_createEvaluatorCacheInternal(
+ eOpenSubdivEvaluator evaluator_type)
+{
+ if (evaluator_type != eOpenSubdivEvaluator::OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) {
+ return nullptr;
+ }
+ OpenSubdiv_EvaluatorCacheImpl *evaluator_cache;
+ evaluator_cache = new OpenSubdiv_EvaluatorCacheImpl;
+ blender::opensubdiv::GpuEvalOutput::EvaluatorCache *eval_cache;
+ eval_cache = new blender::opensubdiv::GpuEvalOutput::EvaluatorCache();
+ evaluator_cache->eval_cache = eval_cache;
+ return evaluator_cache;
+}
+
+void openSubdiv_deleteEvaluatorCacheInternal(OpenSubdiv_EvaluatorCacheImpl *evaluator_cache)
+{
+ delete evaluator_cache;
+}
diff --git a/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.h b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.h
new file mode 100644
index 00000000000..060a78e5cbd
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.h
@@ -0,0 +1,38 @@
+// Copyright 2021 Blender Foundation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+#ifndef OPENSUBDIV_EVALUATOR_CACHE_IMPL_H_
+#define OPENSUBDIV_EVALUATOR_CACHE_IMPL_H_
+
+#include "internal/base/memory.h"
+
+#include "opensubdiv_capi_type.h"
+
+struct OpenSubdiv_EvaluatorCacheImpl {
+ public:
+ OpenSubdiv_EvaluatorCacheImpl();
+ ~OpenSubdiv_EvaluatorCacheImpl();
+
+ void *eval_cache;
+ MEM_CXX_CLASS_ALLOC_FUNCS("OpenSubdiv_EvaluatorCacheImpl");
+};
+
+OpenSubdiv_EvaluatorCacheImpl *openSubdiv_createEvaluatorCacheInternal(
+ eOpenSubdivEvaluator evaluator_type);
+
+void openSubdiv_deleteEvaluatorCacheInternal(OpenSubdiv_EvaluatorCacheImpl *evaluator_cache);
+
+#endif // OPENSUBDIV_EVALUATOR_CACHE_IMPL_H_
diff --git a/intern/opensubdiv/internal/evaluator/evaluator_capi.cc b/intern/opensubdiv/internal/evaluator/evaluator_capi.cc
index 5f8d6cddfd5..567afd3a763 100644
--- a/intern/opensubdiv/internal/evaluator/evaluator_capi.cc
+++ b/intern/opensubdiv/internal/evaluator/evaluator_capi.cc
@@ -18,9 +18,12 @@
#include "opensubdiv_evaluator_capi.h"
+#include <opensubdiv/osd/glslPatchShaderSource.h>
+
#include "MEM_guardedalloc.h"
#include <new>
+#include "internal/evaluator/evaluator_cache_impl.h"
#include "internal/evaluator/evaluator_impl.h"
namespace {
@@ -132,6 +135,74 @@ void evaluateFaceVarying(OpenSubdiv_Evaluator *evaluator,
face_varying_channel, ptex_face_index, face_u, face_v, face_varying);
}
+void getPatchMap(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *patch_map_handles,
+ struct OpenSubdiv_Buffer *patch_map_quadtree,
+ int *min_patch_face,
+ int *max_patch_face,
+ int *max_depth,
+ int *patches_are_triangular)
+{
+ evaluator->impl->eval_output->getPatchMap(patch_map_handles,
+ patch_map_quadtree,
+ min_patch_face,
+ max_patch_face,
+ max_depth,
+ patches_are_triangular);
+}
+
+void fillPatchArraysBuffer(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *patch_array_buffer)
+{
+ evaluator->impl->eval_output->fillPatchArraysBuffer(patch_array_buffer);
+}
+
+void wrapPatchIndexBuffer(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *patch_index_buffer)
+{
+ evaluator->impl->eval_output->wrapPatchIndexBuffer(patch_index_buffer);
+}
+
+void wrapPatchParamBuffer(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *patch_param_buffer)
+{
+ evaluator->impl->eval_output->wrapPatchParamBuffer(patch_param_buffer);
+}
+
+void wrapSrcBuffer(struct OpenSubdiv_Evaluator *evaluator, struct OpenSubdiv_Buffer *src_buffer)
+{
+ evaluator->impl->eval_output->wrapSrcBuffer(src_buffer);
+}
+
+void fillFVarPatchArraysBuffer(struct OpenSubdiv_Evaluator *evaluator,
+ const int face_varying_channel,
+ struct OpenSubdiv_Buffer *patch_array_buffer)
+{
+ evaluator->impl->eval_output->fillFVarPatchArraysBuffer(face_varying_channel,
+ patch_array_buffer);
+}
+
+void wrapFVarPatchIndexBuffer(struct OpenSubdiv_Evaluator *evaluator,
+ const int face_varying_channel,
+ struct OpenSubdiv_Buffer *patch_index_buffer)
+{
+ evaluator->impl->eval_output->wrapFVarPatchIndexBuffer(face_varying_channel, patch_index_buffer);
+}
+
+void wrapFVarPatchParamBuffer(struct OpenSubdiv_Evaluator *evaluator,
+ const int face_varying_channel,
+ struct OpenSubdiv_Buffer *patch_param_buffer)
+{
+ evaluator->impl->eval_output->wrapFVarPatchParamBuffer(face_varying_channel, patch_param_buffer);
+}
+
+void wrapFVarSrcBuffer(struct OpenSubdiv_Evaluator *evaluator,
+ const int face_varying_channel,
+ struct OpenSubdiv_Buffer *src_buffer)
+{
+ evaluator->impl->eval_output->wrapFVarSrcBuffer(face_varying_channel, src_buffer);
+}
+
void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator)
{
evaluator->setCoarsePositions = setCoarsePositions;
@@ -149,16 +220,32 @@ void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator)
evaluator->evaluateFaceVarying = evaluateFaceVarying;
evaluator->evaluatePatchesLimit = evaluatePatchesLimit;
+
+ evaluator->getPatchMap = getPatchMap;
+
+ evaluator->fillPatchArraysBuffer = fillPatchArraysBuffer;
+ evaluator->wrapPatchIndexBuffer = wrapPatchIndexBuffer;
+ evaluator->wrapPatchParamBuffer = wrapPatchParamBuffer;
+ evaluator->wrapSrcBuffer = wrapSrcBuffer;
+
+ evaluator->fillFVarPatchArraysBuffer = fillFVarPatchArraysBuffer;
+ evaluator->wrapFVarPatchIndexBuffer = wrapFVarPatchIndexBuffer;
+ evaluator->wrapFVarPatchParamBuffer = wrapFVarPatchParamBuffer;
+ evaluator->wrapFVarSrcBuffer = wrapFVarSrcBuffer;
}
} // namespace
OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
- OpenSubdiv_TopologyRefiner *topology_refiner)
+ OpenSubdiv_TopologyRefiner *topology_refiner,
+ eOpenSubdivEvaluator evaluator_type,
+ OpenSubdiv_EvaluatorCache *evaluator_cache)
{
OpenSubdiv_Evaluator *evaluator = MEM_new<OpenSubdiv_Evaluator>(__func__);
assignFunctionPointers(evaluator);
- evaluator->impl = openSubdiv_createEvaluatorInternal(topology_refiner);
+ evaluator->impl = openSubdiv_createEvaluatorInternal(
+ topology_refiner, evaluator_type, evaluator_cache ? evaluator_cache->impl : nullptr);
+ evaluator->type = evaluator->impl ? evaluator_type : static_cast<eOpenSubdivEvaluator>(0);
return evaluator;
}
@@ -167,3 +254,30 @@ void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator *evaluator)
openSubdiv_deleteEvaluatorInternal(evaluator->impl);
MEM_delete(evaluator);
}
+
+OpenSubdiv_EvaluatorCache *openSubdiv_createEvaluatorCache(eOpenSubdivEvaluator evaluator_type)
+{
+ OpenSubdiv_EvaluatorCache *evaluator_cache = MEM_new<OpenSubdiv_EvaluatorCache>(__func__);
+ evaluator_cache->impl = openSubdiv_createEvaluatorCacheInternal(evaluator_type);
+ return evaluator_cache;
+}
+
+void openSubdiv_deleteEvaluatorCache(OpenSubdiv_EvaluatorCache *evaluator_cache)
+{
+ if (!evaluator_cache) {
+ return;
+ }
+
+ openSubdiv_deleteEvaluatorCacheInternal(evaluator_cache->impl);
+ MEM_delete(evaluator_cache);
+}
+
+const char *openSubdiv_getGLSLPatchBasisSource(void)
+{
+ /* Using a global string to avoid dealing with memory allocation/ownership. */
+ static std::string patch_basis_source;
+ if (patch_basis_source.empty()) {
+ patch_basis_source = OpenSubdiv::Osd::GLSLPatchShaderSource::GetPatchBasisShaderSource();
+ }
+ return patch_basis_source.c_str();
+}
diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc
index 4f4f332ff15..755b8bfbc81 100644
--- a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc
+++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc
@@ -28,9 +28,6 @@
#include <opensubdiv/far/patchMap.h>
#include <opensubdiv/far/patchTable.h>
#include <opensubdiv/far/patchTableFactory.h>
-#include <opensubdiv/osd/cpuEvaluator.h>
-#include <opensubdiv/osd/cpuPatchTable.h>
-#include <opensubdiv/osd/cpuVertexBuffer.h>
#include <opensubdiv/osd/mesh.h>
#include <opensubdiv/osd/types.h>
#include <opensubdiv/version.h>
@@ -38,19 +35,20 @@
#include "MEM_guardedalloc.h"
#include "internal/base/type.h"
+#include "internal/evaluator/eval_output_cpu.h"
+#include "internal/evaluator/eval_output_gpu.h"
+#include "internal/evaluator/evaluator_cache_impl.h"
+#include "internal/evaluator/patch_map.h"
#include "internal/topology/topology_refiner_impl.h"
+#include "opensubdiv_evaluator_capi.h"
#include "opensubdiv_topology_refiner_capi.h"
-using OpenSubdiv::Far::PatchMap;
using OpenSubdiv::Far::PatchTable;
using OpenSubdiv::Far::PatchTableFactory;
using OpenSubdiv::Far::StencilTable;
using OpenSubdiv::Far::StencilTableFactory;
using OpenSubdiv::Far::TopologyRefiner;
-using OpenSubdiv::Osd::BufferDescriptor;
-using OpenSubdiv::Osd::CpuEvaluator;
-using OpenSubdiv::Osd::CpuPatchTable;
-using OpenSubdiv::Osd::CpuVertexBuffer;
+using OpenSubdiv::Osd::PatchArray;
using OpenSubdiv::Osd::PatchCoord;
namespace blender {
@@ -140,407 +138,9 @@ template<typename T, int kNumMaxElementsOnStack> class StackOrHeapArray {
// 32 is a number of inner vertices along the patch size at subdivision level 6.
typedef StackOrHeapArray<PatchCoord, 32 * 32> StackOrHeapPatchCoordArray;
-// Buffer which implements API required by OpenSubdiv and uses an existing memory as an underlying
-// storage.
-template<typename T> class RawDataWrapperBuffer {
- public:
- RawDataWrapperBuffer(T *data) : data_(data)
- {
- }
-
- T *BindCpuBuffer()
- {
- return data_;
- }
-
- // TODO(sergey): Support UpdateData().
-
- protected:
- T *data_;
-};
-
-template<typename T> class RawDataWrapperVertexBuffer : public RawDataWrapperBuffer<T> {
- public:
- RawDataWrapperVertexBuffer(T *data, int num_vertices)
- : RawDataWrapperBuffer<T>(data), num_vertices_(num_vertices)
- {
- }
-
- int GetNumVertices()
- {
- return num_vertices_;
- }
-
- protected:
- int num_vertices_;
-};
-
-class ConstPatchCoordWrapperBuffer : public RawDataWrapperVertexBuffer<const PatchCoord> {
- public:
- ConstPatchCoordWrapperBuffer(const PatchCoord *data, int num_vertices)
- : RawDataWrapperVertexBuffer(data, num_vertices)
- {
- }
-};
-
-template<typename EVAL_VERTEX_BUFFER,
- typename STENCIL_TABLE,
- typename PATCH_TABLE,
- typename EVALUATOR,
- typename DEVICE_CONTEXT = void>
-class FaceVaryingVolatileEval {
- public:
- typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache;
-
- FaceVaryingVolatileEval(int face_varying_channel,
- const StencilTable *face_varying_stencils,
- int face_varying_width,
- PATCH_TABLE *patch_table,
- EvaluatorCache *evaluator_cache = NULL,
- DEVICE_CONTEXT *device_context = NULL)
- : face_varying_channel_(face_varying_channel),
- src_face_varying_desc_(0, face_varying_width, face_varying_width),
- patch_table_(patch_table),
- evaluator_cache_(evaluator_cache),
- device_context_(device_context)
- {
- using OpenSubdiv::Osd::convertToCompatibleStencilTable;
- num_coarse_face_varying_vertices_ = face_varying_stencils->GetNumControlVertices();
- const int num_total_face_varying_vertices = face_varying_stencils->GetNumControlVertices() +
- face_varying_stencils->GetNumStencils();
- src_face_varying_data_ = EVAL_VERTEX_BUFFER::Create(
- 2, num_total_face_varying_vertices, device_context);
- face_varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(face_varying_stencils,
- device_context_);
- }
-
- ~FaceVaryingVolatileEval()
- {
- delete src_face_varying_data_;
- delete face_varying_stencils_;
- }
-
- void updateData(const float *src, int start_vertex, int num_vertices)
- {
- src_face_varying_data_->UpdateData(src, start_vertex, num_vertices, device_context_);
- }
-
- void refine()
- {
- BufferDescriptor dst_face_varying_desc = src_face_varying_desc_;
- dst_face_varying_desc.offset += num_coarse_face_varying_vertices_ *
- src_face_varying_desc_.stride;
- const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
- evaluator_cache_, src_face_varying_desc_, dst_face_varying_desc, device_context_);
- // in and out points to same buffer so output is put directly after coarse vertices, needed in
- // adaptive mode
- EVALUATOR::EvalStencils(src_face_varying_data_,
- src_face_varying_desc_,
- src_face_varying_data_,
- dst_face_varying_desc,
- face_varying_stencils_,
- eval_instance,
- device_context_);
- }
-
- // NOTE: face_varying must point to a memory of at least float[2]*num_patch_coords.
- void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *face_varying)
- {
- RawDataWrapperBuffer<float> face_varying_data(face_varying);
- BufferDescriptor face_varying_desc(0, 2, 2);
- ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
- const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
- evaluator_cache_, src_face_varying_desc_, face_varying_desc, device_context_);
-
- // src_face_varying_data_ always contains coarse vertices at the beginning.
- // In adaptive mode they are followed by number of blocks for intermediate
- // subdivision levels, and this is what OSD expects in this mode.
- // In non-adaptive mode (generateIntermediateLevels == false),
- // they are followed by max subdivision level, but they break interpolation as OSD
- // expects only one subd level in this buffer.
- // So in non-adaptive mode we put offset into buffer descriptor to skip over coarse vertices.
- BufferDescriptor src_desc = src_face_varying_desc_;
- if (!patch_table_->GetPatchArrayBuffer()[0].GetDescriptor().IsAdaptive()) {
- src_desc.offset += num_coarse_face_varying_vertices_ * src_face_varying_desc_.stride;
- }
-
- EVALUATOR::EvalPatchesFaceVarying(src_face_varying_data_,
- src_desc,
- &face_varying_data,
- face_varying_desc,
- patch_coord_buffer.GetNumVertices(),
- &patch_coord_buffer,
- patch_table_,
- face_varying_channel_,
- eval_instance,
- device_context_);
- }
-
- protected:
- int face_varying_channel_;
-
- BufferDescriptor src_face_varying_desc_;
-
- int num_coarse_face_varying_vertices_;
- EVAL_VERTEX_BUFFER *src_face_varying_data_;
- const STENCIL_TABLE *face_varying_stencils_;
-
- // NOTE: We reference this, do not own it.
- PATCH_TABLE *patch_table_;
-
- EvaluatorCache *evaluator_cache_;
- DEVICE_CONTEXT *device_context_;
-};
-
-// Volatile evaluator which can be used from threads.
-//
-// TODO(sergey): Make it possible to evaluate coordinates in chunks.
-// TODO(sergey): Make it possible to evaluate multiple face varying layers.
-// (or maybe, it's cheap to create new evaluator for existing
-// topology to evaluate all needed face varying layers?)
-template<typename SRC_VERTEX_BUFFER,
- typename EVAL_VERTEX_BUFFER,
- typename STENCIL_TABLE,
- typename PATCH_TABLE,
- typename EVALUATOR,
- typename DEVICE_CONTEXT = void>
-class VolatileEvalOutput {
- public:
- typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache;
- typedef FaceVaryingVolatileEval<EVAL_VERTEX_BUFFER,
- STENCIL_TABLE,
- PATCH_TABLE,
- EVALUATOR,
- DEVICE_CONTEXT>
- FaceVaryingEval;
-
- VolatileEvalOutput(const StencilTable *vertex_stencils,
- const StencilTable *varying_stencils,
- const vector<const StencilTable *> &all_face_varying_stencils,
- const int face_varying_width,
- const PatchTable *patch_table,
- EvaluatorCache *evaluator_cache = NULL,
- DEVICE_CONTEXT *device_context = NULL)
- : src_desc_(0, 3, 3),
- src_varying_desc_(0, 3, 3),
- face_varying_width_(face_varying_width),
- evaluator_cache_(evaluator_cache),
- device_context_(device_context)
- {
- // Total number of vertices = coarse points + refined points + local points.
- int num_total_vertices = vertex_stencils->GetNumControlVertices() +
- vertex_stencils->GetNumStencils();
- num_coarse_vertices_ = vertex_stencils->GetNumControlVertices();
- using OpenSubdiv::Osd::convertToCompatibleStencilTable;
- src_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_);
- src_varying_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_);
- patch_table_ = PATCH_TABLE::Create(patch_table, device_context_);
- vertex_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(vertex_stencils,
- device_context_);
- varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(varying_stencils,
- device_context_);
- // Create evaluators for every face varying channel.
- face_varying_evaluators.reserve(all_face_varying_stencils.size());
- int face_varying_channel = 0;
- for (const StencilTable *face_varying_stencils : all_face_varying_stencils) {
- face_varying_evaluators.push_back(new FaceVaryingEval(face_varying_channel,
- face_varying_stencils,
- face_varying_width,
- patch_table_,
- evaluator_cache_,
- device_context_));
- ++face_varying_channel;
- }
- }
-
- ~VolatileEvalOutput()
- {
- delete src_data_;
- delete src_varying_data_;
- delete patch_table_;
- delete vertex_stencils_;
- delete varying_stencils_;
- for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) {
- delete face_varying_evaluator;
- }
- }
-
- // TODO(sergey): Implement binding API.
-
- void updateData(const float *src, int start_vertex, int num_vertices)
- {
- src_data_->UpdateData(src, start_vertex, num_vertices, device_context_);
- }
-
- void updateVaryingData(const float *src, int start_vertex, int num_vertices)
- {
- src_varying_data_->UpdateData(src, start_vertex, num_vertices, device_context_);
- }
-
- void updateFaceVaryingData(const int face_varying_channel,
- const float *src,
- int start_vertex,
- int num_vertices)
- {
- assert(face_varying_channel >= 0);
- assert(face_varying_channel < face_varying_evaluators.size());
- face_varying_evaluators[face_varying_channel]->updateData(src, start_vertex, num_vertices);
- }
-
- bool hasVaryingData() const
- {
- // return varying_stencils_ != NULL;
- // TODO(sergey): Check this based on actual topology.
- return false;
- }
-
- bool hasFaceVaryingData() const
- {
- return face_varying_evaluators.size() != 0;
- }
-
- void refine()
- {
- // Evaluate vertex positions.
- BufferDescriptor dst_desc = src_desc_;
- dst_desc.offset += num_coarse_vertices_ * src_desc_.stride;
- const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
- evaluator_cache_, src_desc_, dst_desc, device_context_);
- EVALUATOR::EvalStencils(src_data_,
- src_desc_,
- src_data_,
- dst_desc,
- vertex_stencils_,
- eval_instance,
- device_context_);
- // Evaluate varying data.
- if (hasVaryingData()) {
- BufferDescriptor dst_varying_desc = src_varying_desc_;
- dst_varying_desc.offset += num_coarse_vertices_ * src_varying_desc_.stride;
- eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
- evaluator_cache_, src_varying_desc_, dst_varying_desc, device_context_);
- EVALUATOR::EvalStencils(src_varying_data_,
- src_varying_desc_,
- src_varying_data_,
- dst_varying_desc,
- varying_stencils_,
- eval_instance,
- device_context_);
- }
- // Evaluate face-varying data.
- if (hasFaceVaryingData()) {
- for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) {
- face_varying_evaluator->refine();
- }
- }
- }
-
- // NOTE: P must point to a memory of at least float[3]*num_patch_coords.
- void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *P)
- {
- RawDataWrapperBuffer<float> P_data(P);
- // TODO(sergey): Support interleaved vertex-varying data.
- BufferDescriptor P_desc(0, 3, 3);
- ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
- const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
- evaluator_cache_, src_desc_, P_desc, device_context_);
- EVALUATOR::EvalPatches(src_data_,
- src_desc_,
- &P_data,
- P_desc,
- patch_coord_buffer.GetNumVertices(),
- &patch_coord_buffer,
- patch_table_,
- eval_instance,
- device_context_);
- }
-
- // NOTE: P, dPdu, dPdv must point to a memory of at least float[3]*num_patch_coords.
- void evalPatchesWithDerivatives(const PatchCoord *patch_coord,
- const int num_patch_coords,
- float *P,
- float *dPdu,
- float *dPdv)
- {
- assert(dPdu);
- assert(dPdv);
- RawDataWrapperBuffer<float> P_data(P);
- RawDataWrapperBuffer<float> dPdu_data(dPdu), dPdv_data(dPdv);
- // TODO(sergey): Support interleaved vertex-varying data.
- BufferDescriptor P_desc(0, 3, 3);
- BufferDescriptor dpDu_desc(0, 3, 3), pPdv_desc(0, 3, 3);
- ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
- const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
- evaluator_cache_, src_desc_, P_desc, dpDu_desc, pPdv_desc, device_context_);
- EVALUATOR::EvalPatches(src_data_,
- src_desc_,
- &P_data,
- P_desc,
- &dPdu_data,
- dpDu_desc,
- &dPdv_data,
- pPdv_desc,
- patch_coord_buffer.GetNumVertices(),
- &patch_coord_buffer,
- patch_table_,
- eval_instance,
- device_context_);
- }
-
- // NOTE: varying must point to a memory of at least float[3]*num_patch_coords.
- void evalPatchesVarying(const PatchCoord *patch_coord,
- const int num_patch_coords,
- float *varying)
- {
- RawDataWrapperBuffer<float> varying_data(varying);
- BufferDescriptor varying_desc(3, 3, 6);
- ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
- const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
- evaluator_cache_, src_varying_desc_, varying_desc, device_context_);
- EVALUATOR::EvalPatchesVarying(src_varying_data_,
- src_varying_desc_,
- &varying_data,
- varying_desc,
- patch_coord_buffer.GetNumVertices(),
- &patch_coord_buffer,
- patch_table_,
- eval_instance,
- device_context_);
- }
-
- void evalPatchesFaceVarying(const int face_varying_channel,
- const PatchCoord *patch_coord,
- const int num_patch_coords,
- float face_varying[2])
- {
- assert(face_varying_channel >= 0);
- assert(face_varying_channel < face_varying_evaluators.size());
- face_varying_evaluators[face_varying_channel]->evalPatches(
- patch_coord, num_patch_coords, face_varying);
- }
-
- private:
- SRC_VERTEX_BUFFER *src_data_;
- SRC_VERTEX_BUFFER *src_varying_data_;
- PATCH_TABLE *patch_table_;
- BufferDescriptor src_desc_;
- BufferDescriptor src_varying_desc_;
-
- int num_coarse_vertices_;
-
- const STENCIL_TABLE *vertex_stencils_;
- const STENCIL_TABLE *varying_stencils_;
-
- int face_varying_width_;
- vector<FaceVaryingEval *> face_varying_evaluators;
-
- EvaluatorCache *evaluator_cache_;
- DEVICE_CONTEXT *device_context_;
-};
-
void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords,
const int num_patch_coords,
- const OpenSubdiv::Far::PatchMap *patch_map,
+ const PatchMap *patch_map,
StackOrHeapPatchCoordArray *array)
{
array->resize(num_patch_coords);
@@ -553,79 +153,50 @@ void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords,
} // namespace
-// Note: Define as a class instead of typedef to make it possible
-// to have anonymous class in opensubdiv_evaluator_internal.h
-class CpuEvalOutput : public VolatileEvalOutput<CpuVertexBuffer,
- CpuVertexBuffer,
- StencilTable,
- CpuPatchTable,
- CpuEvaluator> {
- public:
- CpuEvalOutput(const StencilTable *vertex_stencils,
- const StencilTable *varying_stencils,
- const vector<const StencilTable *> &all_face_varying_stencils,
- const int face_varying_width,
- const PatchTable *patch_table,
- EvaluatorCache *evaluator_cache = NULL)
- : VolatileEvalOutput<CpuVertexBuffer,
- CpuVertexBuffer,
- StencilTable,
- CpuPatchTable,
- CpuEvaluator>(vertex_stencils,
- varying_stencils,
- all_face_varying_stencils,
- face_varying_width,
- patch_table,
- evaluator_cache)
- {
- }
-};
-
////////////////////////////////////////////////////////////////////////////////
// Evaluator wrapper for anonymous API.
-CpuEvalOutputAPI::CpuEvalOutputAPI(CpuEvalOutput *implementation,
- OpenSubdiv::Far::PatchMap *patch_map)
- : implementation_(implementation), patch_map_(patch_map)
+EvalOutputAPI::EvalOutputAPI(EvalOutput *implementation, PatchMap *patch_map)
+ : patch_map_(patch_map), implementation_(implementation)
{
}
-CpuEvalOutputAPI::~CpuEvalOutputAPI()
+EvalOutputAPI::~EvalOutputAPI()
{
delete implementation_;
}
-void CpuEvalOutputAPI::setCoarsePositions(const float *positions,
- const int start_vertex_index,
- const int num_vertices)
+void EvalOutputAPI::setCoarsePositions(const float *positions,
+ const int start_vertex_index,
+ const int num_vertices)
{
// TODO(sergey): Add sanity check on indices.
implementation_->updateData(positions, start_vertex_index, num_vertices);
}
-void CpuEvalOutputAPI::setVaryingData(const float *varying_data,
- const int start_vertex_index,
- const int num_vertices)
+void EvalOutputAPI::setVaryingData(const float *varying_data,
+ const int start_vertex_index,
+ const int num_vertices)
{
// TODO(sergey): Add sanity check on indices.
implementation_->updateVaryingData(varying_data, start_vertex_index, num_vertices);
}
-void CpuEvalOutputAPI::setFaceVaryingData(const int face_varying_channel,
- const float *face_varying_data,
- const int start_vertex_index,
- const int num_vertices)
+void EvalOutputAPI::setFaceVaryingData(const int face_varying_channel,
+ const float *face_varying_data,
+ const int start_vertex_index,
+ const int num_vertices)
{
// TODO(sergey): Add sanity check on indices.
implementation_->updateFaceVaryingData(
face_varying_channel, face_varying_data, start_vertex_index, num_vertices);
}
-void CpuEvalOutputAPI::setCoarsePositionsFromBuffer(const void *buffer,
- const int start_offset,
- const int stride,
- const int start_vertex_index,
- const int num_vertices)
+void EvalOutputAPI::setCoarsePositionsFromBuffer(const void *buffer,
+ const int start_offset,
+ const int stride,
+ const int start_vertex_index,
+ const int num_vertices)
{
// TODO(sergey): Add sanity check on indices.
const unsigned char *current_buffer = (unsigned char *)buffer;
@@ -638,11 +209,11 @@ void CpuEvalOutputAPI::setCoarsePositionsFromBuffer(const void *buffer,
}
}
-void CpuEvalOutputAPI::setVaryingDataFromBuffer(const void *buffer,
- const int start_offset,
- const int stride,
- const int start_vertex_index,
- const int num_vertices)
+void EvalOutputAPI::setVaryingDataFromBuffer(const void *buffer,
+ const int start_offset,
+ const int stride,
+ const int start_vertex_index,
+ const int num_vertices)
{
// TODO(sergey): Add sanity check on indices.
const unsigned char *current_buffer = (unsigned char *)buffer;
@@ -655,12 +226,12 @@ void CpuEvalOutputAPI::setVaryingDataFromBuffer(const void *buffer,
}
}
-void CpuEvalOutputAPI::setFaceVaryingDataFromBuffer(const int face_varying_channel,
- const void *buffer,
- const int start_offset,
- const int stride,
- const int start_vertex_index,
- const int num_vertices)
+void EvalOutputAPI::setFaceVaryingDataFromBuffer(const int face_varying_channel,
+ const void *buffer,
+ const int start_offset,
+ const int stride,
+ const int start_vertex_index,
+ const int num_vertices)
{
// TODO(sergey): Add sanity check on indices.
const unsigned char *current_buffer = (unsigned char *)buffer;
@@ -675,17 +246,17 @@ void CpuEvalOutputAPI::setFaceVaryingDataFromBuffer(const int face_varying_chann
}
}
-void CpuEvalOutputAPI::refine()
+void EvalOutputAPI::refine()
{
implementation_->refine();
}
-void CpuEvalOutputAPI::evaluateLimit(const int ptex_face_index,
- float face_u,
- float face_v,
- float P[3],
- float dPdu[3],
- float dPdv[3])
+void EvalOutputAPI::evaluateLimit(const int ptex_face_index,
+ float face_u,
+ float face_v,
+ float P[3],
+ float dPdu[3],
+ float dPdv[3])
{
assert(face_u >= 0.0f);
assert(face_u <= 1.0f);
@@ -701,10 +272,10 @@ void CpuEvalOutputAPI::evaluateLimit(const int ptex_face_index,
}
}
-void CpuEvalOutputAPI::evaluateVarying(const int ptex_face_index,
- float face_u,
- float face_v,
- float varying[3])
+void EvalOutputAPI::evaluateVarying(const int ptex_face_index,
+ float face_u,
+ float face_v,
+ float varying[3])
{
assert(face_u >= 0.0f);
assert(face_u <= 1.0f);
@@ -715,11 +286,11 @@ void CpuEvalOutputAPI::evaluateVarying(const int ptex_face_index,
implementation_->evalPatchesVarying(&patch_coord, 1, varying);
}
-void CpuEvalOutputAPI::evaluateFaceVarying(const int face_varying_channel,
- const int ptex_face_index,
- float face_u,
- float face_v,
- float face_varying[2])
+void EvalOutputAPI::evaluateFaceVarying(const int face_varying_channel,
+ const int ptex_face_index,
+ float face_u,
+ float face_v,
+ float face_varying[2])
{
assert(face_u >= 0.0f);
assert(face_u <= 1.0f);
@@ -730,11 +301,11 @@ 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)
+void EvalOutputAPI::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);
@@ -747,6 +318,73 @@ void CpuEvalOutputAPI::evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_c
}
}
+void EvalOutputAPI::getPatchMap(OpenSubdiv_Buffer *patch_map_handles,
+ OpenSubdiv_Buffer *patch_map_quadtree,
+ int *min_patch_face,
+ int *max_patch_face,
+ int *max_depth,
+ int *patches_are_triangular)
+{
+ *min_patch_face = patch_map_->getMinPatchFace();
+ *max_patch_face = patch_map_->getMaxPatchFace();
+ *max_depth = patch_map_->getMaxDepth();
+ *patches_are_triangular = patch_map_->getPatchesAreTriangular();
+
+ const std::vector<PatchTable::PatchHandle> &handles = patch_map_->getHandles();
+ PatchTable::PatchHandle *buffer_handles = static_cast<PatchTable::PatchHandle *>(
+ patch_map_handles->alloc(patch_map_handles, handles.size()));
+ memcpy(buffer_handles, &handles[0], sizeof(PatchTable::PatchHandle) * handles.size());
+
+ const std::vector<PatchMap::QuadNode> &quadtree = patch_map_->nodes();
+ PatchMap::QuadNode *buffer_nodes = static_cast<PatchMap::QuadNode *>(
+ patch_map_quadtree->alloc(patch_map_quadtree, quadtree.size()));
+ memcpy(buffer_nodes, &quadtree[0], sizeof(PatchMap::QuadNode) * quadtree.size());
+}
+
+void EvalOutputAPI::fillPatchArraysBuffer(OpenSubdiv_Buffer *patch_arrays_buffer)
+{
+ implementation_->fillPatchArraysBuffer(patch_arrays_buffer);
+}
+
+void EvalOutputAPI::wrapPatchIndexBuffer(OpenSubdiv_Buffer *patch_index_buffer)
+{
+ implementation_->wrapPatchIndexBuffer(patch_index_buffer);
+}
+
+void EvalOutputAPI::wrapPatchParamBuffer(OpenSubdiv_Buffer *patch_param_buffer)
+{
+ implementation_->wrapPatchParamBuffer(patch_param_buffer);
+}
+
+void EvalOutputAPI::wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer)
+{
+ implementation_->wrapSrcBuffer(src_buffer);
+}
+
+void EvalOutputAPI::fillFVarPatchArraysBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_arrays_buffer)
+{
+ implementation_->fillFVarPatchArraysBuffer(face_varying_channel, patch_arrays_buffer);
+}
+
+void EvalOutputAPI::wrapFVarPatchIndexBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_index_buffer)
+{
+ implementation_->wrapFVarPatchIndexBuffer(face_varying_channel, patch_index_buffer);
+}
+
+void EvalOutputAPI::wrapFVarPatchParamBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_param_buffer)
+{
+ implementation_->wrapFVarPatchParamBuffer(face_varying_channel, patch_param_buffer);
+}
+
+void EvalOutputAPI::wrapFVarSrcBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *src_buffer)
+{
+ implementation_->wrapFVarSrcBuffer(face_varying_channel, src_buffer);
+}
+
} // namespace opensubdiv
} // namespace blender
@@ -763,8 +401,15 @@ OpenSubdiv_EvaluatorImpl::~OpenSubdiv_EvaluatorImpl()
}
OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal(
- OpenSubdiv_TopologyRefiner *topology_refiner)
+ OpenSubdiv_TopologyRefiner *topology_refiner,
+ eOpenSubdivEvaluator evaluator_type,
+ OpenSubdiv_EvaluatorCacheImpl *evaluator_cache_descr)
{
+ // Only CPU and GLCompute are implemented at the moment.
+ if (evaluator_type != OPENSUBDIV_EVALUATOR_CPU &&
+ evaluator_type != OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) {
+ return NULL;
+ }
using blender::opensubdiv::vector;
TopologyRefiner *refiner = topology_refiner->impl->topology_refiner;
if (refiner == NULL) {
@@ -867,14 +512,34 @@ OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal(
}
}
// Create OpenSubdiv's CPU side evaluator.
- // TODO(sergey): Make it possible to use different evaluators.
- blender::opensubdiv::CpuEvalOutput *eval_output = new blender::opensubdiv::CpuEvalOutput(
- vertex_stencils, varying_stencils, all_face_varying_stencils, 2, patch_table);
- OpenSubdiv::Far::PatchMap *patch_map = new PatchMap(*patch_table);
+ blender::opensubdiv::EvalOutputAPI::EvalOutput *eval_output = nullptr;
+
+ const bool use_gl_evaluator = evaluator_type == OPENSUBDIV_EVALUATOR_GLSL_COMPUTE;
+ if (use_gl_evaluator) {
+ blender::opensubdiv::GpuEvalOutput::EvaluatorCache *evaluator_cache = nullptr;
+ if (evaluator_cache_descr) {
+ evaluator_cache = static_cast<blender::opensubdiv::GpuEvalOutput::EvaluatorCache *>(
+ evaluator_cache_descr->eval_cache);
+ }
+
+ eval_output = new blender::opensubdiv::GpuEvalOutput(vertex_stencils,
+ varying_stencils,
+ all_face_varying_stencils,
+ 2,
+ patch_table,
+ evaluator_cache);
+ }
+ else {
+ eval_output = new blender::opensubdiv::CpuEvalOutput(
+ vertex_stencils, varying_stencils, all_face_varying_stencils, 2, patch_table);
+ }
+
+ blender::opensubdiv::PatchMap *patch_map = new blender::opensubdiv::PatchMap(*patch_table);
// Wrap everything we need into an object which we control from our side.
OpenSubdiv_EvaluatorImpl *evaluator_descr;
evaluator_descr = new OpenSubdiv_EvaluatorImpl();
- evaluator_descr->eval_output = new blender::opensubdiv::CpuEvalOutputAPI(eval_output, patch_map);
+
+ evaluator_descr->eval_output = new blender::opensubdiv::EvalOutputAPI(eval_output, patch_map);
evaluator_descr->patch_map = patch_map;
evaluator_descr->patch_table = patch_table;
// TOOD(sergey): Look into whether we've got duplicated stencils arrays.
diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.h b/intern/opensubdiv/internal/evaluator/evaluator_impl.h
index e7ccc9b376a..e24d47cba79 100644
--- a/intern/opensubdiv/internal/evaluator/evaluator_impl.h
+++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.h
@@ -28,14 +28,17 @@
#include "internal/base/memory.h"
+#include "opensubdiv_capi_type.h"
+
+struct OpenSubdiv_Buffer;
+struct OpenSubdiv_EvaluatorCacheImpl;
struct OpenSubdiv_PatchCoord;
struct OpenSubdiv_TopologyRefiner;
namespace blender {
namespace opensubdiv {
-// Anonymous forward declaration of actual evaluator implementation.
-class CpuEvalOutput;
+class PatchMap;
// Wrapper around implementation, which defines API which we are capable to
// provide over the implementation.
@@ -43,11 +46,15 @@ class CpuEvalOutput;
// TODO(sergey): It is almost the same as C-API object, so ideally need to
// merge them somehow, but how to do this and keep files with all the templates
// and such separate?
-class CpuEvalOutputAPI {
+class EvalOutputAPI {
public:
- // NOTE: API object becomes an owner of evaluator. Patch we are referencing.
- CpuEvalOutputAPI(CpuEvalOutput *implementation, OpenSubdiv::Far::PatchMap *patch_map);
- ~CpuEvalOutputAPI();
+ // Anonymous forward declaration of actual evaluator implementation.
+ class EvalOutput;
+
+ // NOTE: PatchMap is not owned, only referenced.
+ EvalOutputAPI(EvalOutput *implementation, PatchMap *patch_map);
+
+ ~EvalOutputAPI();
// Set coarse positions from a continuous array of coordinates.
void setCoarsePositions(const float *positions,
@@ -130,9 +137,47 @@ class CpuEvalOutputAPI {
float *dPdu,
float *dPdv);
+ // Fill the output buffers and variables with data from the PatchMap.
+ void getPatchMap(OpenSubdiv_Buffer *patch_map_handles,
+ OpenSubdiv_Buffer *patch_map_quadtree,
+ int *min_patch_face,
+ int *max_patch_face,
+ int *max_depth,
+ int *patches_are_triangular);
+
+ // Copy the patch arrays buffer used by OpenSubDiv for the source data to the given buffer.
+ void fillPatchArraysBuffer(OpenSubdiv_Buffer *patch_arrays_buffer);
+
+ // Wrap the patch index buffer used by OpenSubDiv for the source data with the given buffer.
+ void wrapPatchIndexBuffer(OpenSubdiv_Buffer *patch_index_buffer);
+
+ // Wrap the patch param buffer used by OpenSubDiv for the source data with the given buffer.
+ void wrapPatchParamBuffer(OpenSubdiv_Buffer *patch_param_buffer);
+
+ // Wrap the buffer used by OpenSubDiv for the source data with the given buffer.
+ void wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer);
+
+ // Copy the patch arrays buffer used by OpenSubDiv for the face varying channel with the given
+ // buffer.
+ void fillFVarPatchArraysBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_arrays_buffer);
+
+ // Wrap the patch index buffer used by OpenSubDiv for the face varying channel with the given
+ // buffer.
+ void wrapFVarPatchIndexBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_index_buffer);
+
+ // Wrap the patch param buffer used by OpenSubDiv for the face varying channel with the given
+ // buffer.
+ void wrapFVarPatchParamBuffer(const int face_varying_channel,
+ OpenSubdiv_Buffer *patch_param_buffer);
+
+ // Wrap thebuffer used by OpenSubDiv for the face varying channel with the given buffer.
+ void wrapFVarSrcBuffer(const int face_varying_channel, OpenSubdiv_Buffer *src_buffer);
+
protected:
- CpuEvalOutput *implementation_;
- OpenSubdiv::Far::PatchMap *patch_map_;
+ PatchMap *patch_map_;
+ EvalOutput *implementation_;
};
} // namespace opensubdiv
@@ -143,15 +188,17 @@ struct OpenSubdiv_EvaluatorImpl {
OpenSubdiv_EvaluatorImpl();
~OpenSubdiv_EvaluatorImpl();
- blender::opensubdiv::CpuEvalOutputAPI *eval_output;
- const OpenSubdiv::Far::PatchMap *patch_map;
+ blender::opensubdiv::EvalOutputAPI *eval_output;
+ const blender::opensubdiv::PatchMap *patch_map;
const OpenSubdiv::Far::PatchTable *patch_table;
MEM_CXX_CLASS_ALLOC_FUNCS("OpenSubdiv_EvaluatorImpl");
};
OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal(
- struct OpenSubdiv_TopologyRefiner *topology_refiner);
+ struct OpenSubdiv_TopologyRefiner *topology_refiner,
+ eOpenSubdivEvaluator evaluator_type,
+ OpenSubdiv_EvaluatorCacheImpl *evaluator_cache_descr);
void openSubdiv_deleteEvaluatorInternal(OpenSubdiv_EvaluatorImpl *evaluator);
diff --git a/intern/opensubdiv/internal/evaluator/patch_map.cc b/intern/opensubdiv/internal/evaluator/patch_map.cc
new file mode 100644
index 00000000000..2ebdb922326
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/patch_map.cc
@@ -0,0 +1,212 @@
+// Original code 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.
+//
+// Modifications copyright 2021 Blender Foundation. All rights reserved.
+
+#include "internal/evaluator/patch_map.h"
+
+using OpenSubdiv::Far::ConstPatchParamArray;
+using OpenSubdiv::Far::Index;
+using OpenSubdiv::Far::PatchParam;
+using OpenSubdiv::Far::PatchParamTable;
+using OpenSubdiv::Far::PatchTable;
+
+namespace blender {
+namespace opensubdiv {
+
+//
+// Inline quadtree assembly methods used by the constructor:
+//
+
+// sets all the children to point to the patch of given index
+inline void PatchMap::QuadNode::SetChildren(int index)
+{
+
+ for (int i = 0; i < 4; ++i) {
+ children[i].isSet = true;
+ children[i].isLeaf = true;
+ children[i].index = index;
+ }
+}
+
+// sets the child in "quadrant" to point to the node or patch of the given index
+inline void PatchMap::QuadNode::SetChild(int quadrant, int index, bool isLeaf)
+{
+
+ assert(!children[quadrant].isSet);
+ children[quadrant].isSet = true;
+ children[quadrant].isLeaf = isLeaf;
+ children[quadrant].index = index;
+}
+
+inline void PatchMap::assignRootNode(QuadNode *node, int index)
+{
+
+ // Assign the given index to all children of the node (all leaves)
+ node->SetChildren(index);
+}
+
+inline PatchMap::QuadNode *PatchMap::assignLeafOrChildNode(QuadNode *node,
+ bool isLeaf,
+ int quadrant,
+ int index)
+{
+
+ // Assign the node given if it is a leaf node, otherwise traverse
+ // the node -- creating/assigning a new child node if needed
+
+ if (isLeaf) {
+ node->SetChild(quadrant, index, true);
+ return node;
+ }
+ if (node->children[quadrant].isSet) {
+ return &_quadtree[node->children[quadrant].index];
+ }
+ else {
+ int newChildNodeIndex = (int)_quadtree.size();
+ _quadtree.push_back(QuadNode());
+ node->SetChild(quadrant, newChildNodeIndex, false);
+ return &_quadtree[newChildNodeIndex];
+ }
+}
+
+//
+// Constructor and initialization methods for the handles and quadtree:
+//
+PatchMap::PatchMap(PatchTable const &patchTable)
+ : _minPatchFace(-1), _maxPatchFace(-1), _maxDepth(0)
+{
+
+ _patchesAreTriangular = patchTable.GetVaryingPatchDescriptor().GetNumControlVertices() == 3;
+
+ if (patchTable.GetNumPatchesTotal() > 0) {
+ initializeHandles(patchTable);
+ initializeQuadtree(patchTable);
+ }
+}
+
+void PatchMap::initializeHandles(PatchTable const &patchTable)
+{
+
+ //
+ // Populate the vector of patch Handles. Keep track of the min and max
+ // face indices to allocate resources accordingly and limit queries:
+ //
+ _minPatchFace = (int)patchTable.GetPatchParamTable()[0].GetFaceId();
+ _maxPatchFace = _minPatchFace;
+
+ int numArrays = (int)patchTable.GetNumPatchArrays();
+ int numPatches = (int)patchTable.GetNumPatchesTotal();
+
+ _handles.resize(numPatches);
+
+ for (int pArray = 0, handleIndex = 0; pArray < numArrays; ++pArray) {
+
+ ConstPatchParamArray params = patchTable.GetPatchParams(pArray);
+
+ int patchSize = patchTable.GetPatchArrayDescriptor(pArray).GetNumControlVertices();
+
+ for (Index j = 0; j < patchTable.GetNumPatches(pArray); ++j, ++handleIndex) {
+
+ Handle &h = _handles[handleIndex];
+
+ h.arrayIndex = pArray;
+ h.patchIndex = handleIndex;
+ h.vertIndex = j * patchSize;
+
+ int patchFaceId = params[j].GetFaceId();
+ _minPatchFace = std::min(_minPatchFace, patchFaceId);
+ _maxPatchFace = std::max(_maxPatchFace, patchFaceId);
+ }
+ }
+}
+
+void PatchMap::initializeQuadtree(PatchTable const &patchTable)
+{
+
+ //
+ // Reserve quadtree nodes for the worst case and prune later. Set the
+ // initial size to accomodate the root node of each patch face:
+ //
+ int nPatchFaces = (_maxPatchFace - _minPatchFace) + 1;
+
+ int nHandles = (int)_handles.size();
+
+ _quadtree.reserve(nPatchFaces + nHandles);
+ _quadtree.resize(nPatchFaces);
+
+ PatchParamTable const &params = patchTable.GetPatchParamTable();
+
+ for (int handle = 0; handle < nHandles; ++handle) {
+
+ PatchParam const &param = params[handle];
+
+ int depth = param.GetDepth();
+ int rootDepth = param.NonQuadRoot();
+
+ _maxDepth = std::max(_maxDepth, depth);
+
+ QuadNode *node = &_quadtree[param.GetFaceId() - _minPatchFace];
+
+ if (depth == rootDepth) {
+ assignRootNode(node, handle);
+ continue;
+ }
+
+ if (!_patchesAreTriangular) {
+ // Use the UV bits of the PatchParam directly for quad patches:
+ int u = param.GetU();
+ int v = param.GetV();
+
+ for (int j = rootDepth + 1; j <= depth; ++j) {
+ int uBit = (u >> (depth - j)) & 1;
+ int vBit = (v >> (depth - j)) & 1;
+
+ int quadrant = (vBit << 1) | uBit;
+
+ node = assignLeafOrChildNode(node, (j == depth), quadrant, handle);
+ }
+ }
+ else {
+ // Use an interior UV point of triangles to identify quadrants:
+ double u = 0.25;
+ double v = 0.25;
+ param.UnnormalizeTriangle(u, v);
+
+ double median = 0.5;
+ bool triRotated = false;
+
+ for (int j = rootDepth + 1; j <= depth; ++j, median *= 0.5) {
+ int quadrant = transformUVToTriQuadrant(median, u, v, triRotated);
+
+ node = assignLeafOrChildNode(node, (j == depth), quadrant, handle);
+ }
+ }
+ }
+
+ // Swap the Node vector with a copy to reduce worst case memory allocation:
+ QuadTree tmpTree = _quadtree;
+ _quadtree.swap(tmpTree);
+}
+
+} // namespace opensubdiv
+} // namespace blender
diff --git a/intern/opensubdiv/internal/evaluator/patch_map.h b/intern/opensubdiv/internal/evaluator/patch_map.h
new file mode 100644
index 00000000000..af804d6ca71
--- /dev/null
+++ b/intern/opensubdiv/internal/evaluator/patch_map.h
@@ -0,0 +1,264 @@
+// Original code 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.
+//
+// Modifications copyright 2021 Blender Foundation. All rights reserved.
+
+#ifndef OPENSUBDIV_PATCH_MAP_H_
+#define OPENSUBDIV_PATCH_MAP_H_
+
+#include <opensubdiv/far/patchTable.h>
+
+namespace blender {
+namespace opensubdiv {
+
+/// \brief An quadtree-based map connecting coarse faces to their sub-patches
+///
+/// PatchTable::PatchArrays contain lists of patches that represent the limit
+/// surface of a mesh, sorted by their topological type. These arrays break the
+/// connection between coarse faces and their sub-patches.
+///
+/// The PatchMap provides a quad-tree based lookup structure that, given a singular
+/// parametric location, can efficiently return a handle to the sub-patch that
+/// contains this location.
+///
+class PatchMap {
+ public:
+ // Quadtree node with 4 children, tree is just a vector of nodes
+ struct QuadNode {
+ QuadNode()
+ {
+ std::memset(this, 0, sizeof(QuadNode));
+ }
+
+ struct Child {
+ unsigned int isSet : 1; // true if the child has been set
+ unsigned int isLeaf : 1; // true if the child is a QuadNode
+ unsigned int index : 30; // child index (either QuadNode or Handle)
+ };
+
+ // sets all the children to point to the patch of given index
+ void SetChildren(int index);
+
+ // sets the child in "quadrant" to point to the node or patch of the given index
+ void SetChild(int quadrant, int index, bool isLeaf);
+
+ Child children[4];
+ };
+
+ typedef OpenSubdiv::Far::PatchTable::PatchHandle Handle;
+
+ /// \brief Constructor
+ ///
+ /// @param patchTable A valid PatchTable
+ ///
+ PatchMap(OpenSubdiv::Far::PatchTable const &patchTable);
+
+ /// \brief Returns a handle to the sub-patch of the face at the given (u,v).
+ /// Note that the patch face ID corresponds to potentially quadrangulated
+ /// face indices and not the base face indices (see Far::PtexIndices for more
+ /// details).
+ ///
+ /// @param patchFaceId The index of the patch (Ptex) face
+ ///
+ /// @param u Local u parameter
+ ///
+ /// @param v Local v parameter
+ ///
+ /// @return A patch handle or 0 if the face is not supported (index
+ /// out of bounds) or is tagged as a hole
+ ///
+ Handle const *FindPatch(int patchFaceId, double u, double v) const;
+
+ int getMinPatchFace() const
+ {
+ return _minPatchFace;
+ }
+
+ int getMaxPatchFace() const
+ {
+ return _maxPatchFace;
+ }
+
+ int getMaxDepth() const
+ {
+ return _maxDepth;
+ }
+
+ bool getPatchesAreTriangular() const
+ {
+ return _patchesAreTriangular;
+ }
+
+ const std::vector<Handle> &getHandles()
+ {
+ return _handles;
+ }
+
+ const std::vector<QuadNode> &nodes()
+ {
+ return _quadtree;
+ }
+
+ private:
+ void initializeHandles(OpenSubdiv::Far::PatchTable const &patchTable);
+ void initializeQuadtree(OpenSubdiv::Far::PatchTable const &patchTable);
+
+ typedef std::vector<QuadNode> QuadTree;
+
+ // Internal methods supporting quadtree construction and queries
+ void assignRootNode(QuadNode *node, int index);
+ QuadNode *assignLeafOrChildNode(QuadNode *node, bool isLeaf, int quad, int index);
+
+ template<class T> static int transformUVToQuadQuadrant(T const &median, T &u, T &v);
+ template<class T>
+ static int transformUVToTriQuadrant(T const &median, T &u, T &v, bool &rotated);
+
+ private:
+ bool _patchesAreTriangular; // tri and quad assembly and search requirements differ
+
+ int _minPatchFace; // minimum patch face index supported by the map
+ int _maxPatchFace; // maximum patch face index supported by the map
+ int _maxDepth; // maximum depth of a patch in the tree
+
+ std::vector<Handle> _handles; // all the patches in the PatchTable
+ std::vector<QuadNode> _quadtree; // quadtree nodes
+};
+
+//
+// Given a median value for both U and V, these methods transform a (u,v) pair
+// into the quadrant that contains them and returns the quadrant index.
+//
+// Quadrant indexing for tri and quad patches -- consistent with PatchParam's
+// usage of UV bits:
+//
+// (0,1) o-----o-----o (1,1) (0,1) o (1,0) o-----o-----o (0,0)
+// | | | |\ \ 1 |\ 0 |
+// | 2 | 3 | | \ \ | \ |
+// | | | | 2 \ \| 3 \|
+// o-----o-----o o-----o o-----o
+// | | | |\ 3 |\ \ 2 |
+// | 0 | 1 | | \ | \ \ |
+// | | | | 0 \| 1 \ \|
+// (0,0) o-----o-----o (1,0) (0,0) o-----o-----o (1,0) o (0,1)
+//
+// The triangular case also takes and returns/affects the rotation of the
+// quadrant being searched and identified (quadrant 3 imparts a rotation).
+//
+template<class T> inline int PatchMap::transformUVToQuadQuadrant(T const &median, T &u, T &v)
+{
+
+ int uHalf = (u >= median);
+ if (uHalf)
+ u -= median;
+
+ int vHalf = (v >= median);
+ if (vHalf)
+ v -= median;
+
+ return (vHalf << 1) | uHalf;
+}
+
+template<class T>
+int inline PatchMap::transformUVToTriQuadrant(T const &median, T &u, T &v, bool &rotated)
+{
+
+ if (!rotated) {
+ if (u >= median) {
+ u -= median;
+ return 1;
+ }
+ if (v >= median) {
+ v -= median;
+ return 2;
+ }
+ if ((u + v) >= median) {
+ rotated = true;
+ return 3;
+ }
+ return 0;
+ }
+ else {
+ if (u < median) {
+ v -= median;
+ return 1;
+ }
+ if (v < median) {
+ u -= median;
+ return 2;
+ }
+ u -= median;
+ v -= median;
+ if ((u + v) < median) {
+ rotated = false;
+ return 3;
+ }
+ return 0;
+ }
+}
+
+/// Returns a handle to the sub-patch of the face at the given (u,v).
+inline PatchMap::Handle const *PatchMap::FindPatch(int faceid, double u, double v) const
+{
+
+ //
+ // Reject patch faces not supported by this map, or those corresponding
+ // to holes or otherwise unassigned (the root node for a patch will
+ // have all or no quadrants set):
+ //
+ if ((faceid < _minPatchFace) || (faceid > _maxPatchFace))
+ return 0;
+
+ QuadNode const *node = &_quadtree[faceid - _minPatchFace];
+
+ if (!node->children[0].isSet)
+ return 0;
+
+ //
+ // Search the tree for the sub-patch containing the given (u,v)
+ //
+ assert((u >= 0.0) && (u <= 1.0) && (v >= 0.0) && (v <= 1.0));
+
+ double median = 0.5;
+ bool triRotated = false;
+
+ for (int depth = 0; depth <= _maxDepth; ++depth, median *= 0.5) {
+
+ int quadrant = _patchesAreTriangular ? transformUVToTriQuadrant(median, u, v, triRotated) :
+ transformUVToQuadQuadrant(median, u, v);
+
+ // holes should have been rejected at the root node of the face
+ assert(node->children[quadrant].isSet);
+
+ if (node->children[quadrant].isLeaf) {
+ return &_handles[node->children[quadrant].index];
+ }
+ else {
+ node = &_quadtree[node->children[quadrant].index];
+ }
+ }
+ assert(0);
+ return 0;
+}
+} // namespace opensubdiv
+} // namespace blender
+
+#endif // OPENSUBDIV_PATCH_MAP_H_
diff --git a/intern/opensubdiv/opensubdiv_evaluator_capi.h b/intern/opensubdiv/opensubdiv_evaluator_capi.h
index b860ae8db2e..1afd3e966f6 100644
--- a/intern/opensubdiv/opensubdiv_evaluator_capi.h
+++ b/intern/opensubdiv/opensubdiv_evaluator_capi.h
@@ -19,6 +19,10 @@
#ifndef OPENSUBDIV_EVALUATOR_CAPI_H_
#define OPENSUBDIV_EVALUATOR_CAPI_H_
+#include <stdint.h> // for uint64_t
+
+#include "opensubdiv_capi_type.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -27,6 +31,38 @@ struct OpenSubdiv_EvaluatorInternal;
struct OpenSubdiv_PatchCoord;
struct OpenSubdiv_TopologyRefiner;
+// Callback type for doing input/output operations on buffers.
+// Useful to abstract GPU buffers.
+typedef struct OpenSubdiv_Buffer {
+ // Bind the buffer to the GPU.
+ void (*bind_gpu)(const struct OpenSubdiv_Buffer *buffer);
+
+ // Allocate the buffer directly on the host for the given size in bytes. This has to return
+ // a pointer to the newly allocated memory.
+ void *(*alloc)(const struct OpenSubdiv_Buffer *buffer, const unsigned int size);
+
+ // Allocate the buffer directly on the device for the given size in bytes.
+ void (*device_alloc)(const struct OpenSubdiv_Buffer *buffer, const unsigned int size);
+
+ // Update the given range of the buffer with new data.
+ void (*device_update)(const struct OpenSubdiv_Buffer *buffer,
+ unsigned int start,
+ unsigned int len,
+ const void *data);
+
+ // Wrap an existing GPU buffer, given its device handle, into the client's buffer type for
+ // read-only use.
+ void (*wrap_device_handle)(const struct OpenSubdiv_Buffer *buffer, uint64_t device_ptr);
+
+ // Offset in the buffer where the data starts, if a single buffer is used for multiple data
+ // channels.
+ int buffer_offset;
+
+ // Pointer to the client buffer data, which is modified or initialized through the various
+ // callbacks.
+ void *data;
+} OpenSubdiv_Buffer;
+
typedef struct OpenSubdiv_Evaluator {
// Set coarse positions from a continuous array of coordinates.
void (*setCoarsePositions)(struct OpenSubdiv_Evaluator *evaluator,
@@ -122,15 +158,78 @@ typedef struct OpenSubdiv_Evaluator {
float *dPdu,
float *dPdv);
+ // Copy the patch map to the given buffers, and output some topology information.
+ void (*getPatchMap)(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *patch_map_handles,
+ struct OpenSubdiv_Buffer *patch_map_quadtree,
+ int *min_patch_face,
+ int *max_patch_face,
+ int *max_depth,
+ int *patches_are_triangular);
+
+ // Fill the given buffer with data from the evaluator's patch array buffer.
+ void (*fillPatchArraysBuffer)(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *patch_array_buffer);
+
+ // Fill the given buffer with data from the evaluator's patch index buffer.
+ void (*wrapPatchIndexBuffer)(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *patch_index_buffer);
+
+ // Fill the given buffer with data from the evaluator's patch parameter buffer.
+ void (*wrapPatchParamBuffer)(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *patch_param_buffer);
+
+ // Fill the given buffer with data from the evaluator's source buffer.
+ void (*wrapSrcBuffer)(struct OpenSubdiv_Evaluator *evaluator,
+ struct OpenSubdiv_Buffer *src_buffer);
+
+ // Fill the given buffer with data from the evaluator's face varying patch array buffer.
+ void (*fillFVarPatchArraysBuffer)(struct OpenSubdiv_Evaluator *evaluator,
+ const int face_varying_channel,
+ struct OpenSubdiv_Buffer *patch_array_buffer);
+
+ // Fill the given buffer with data from the evaluator's face varying patch index buffer.
+ void (*wrapFVarPatchIndexBuffer)(struct OpenSubdiv_Evaluator *evaluator,
+ const int face_varying_channel,
+ struct OpenSubdiv_Buffer *patch_index_buffer);
+
+ // Fill the given buffer with data from the evaluator's face varying patch parameter buffer.
+ void (*wrapFVarPatchParamBuffer)(struct OpenSubdiv_Evaluator *evaluator,
+ const int face_varying_channel,
+ struct OpenSubdiv_Buffer *patch_param_buffer);
+
+ // Fill the given buffer with data from the evaluator's face varying source buffer.
+ void (*wrapFVarSrcBuffer)(struct OpenSubdiv_Evaluator *evaluator,
+ const int face_varying_channel,
+ struct OpenSubdiv_Buffer *src_buffer);
+
// Implementation of the evaluator.
struct OpenSubdiv_EvaluatorImpl *impl;
+
+ // Type of the evaluator.
+ eOpenSubdivEvaluator type;
} OpenSubdiv_Evaluator;
+typedef struct OpenSubdiv_EvaluatorCache {
+ // Implementation of the evaluator cache.
+ struct OpenSubdiv_EvaluatorCacheImpl *impl;
+} OpenSubdiv_EvaluatorCache;
+
OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
- struct OpenSubdiv_TopologyRefiner *topology_refiner);
+ struct OpenSubdiv_TopologyRefiner *topology_refiner,
+ eOpenSubdivEvaluator evaluator_type,
+ OpenSubdiv_EvaluatorCache *evaluator_cache);
void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator *evaluator);
+OpenSubdiv_EvaluatorCache *openSubdiv_createEvaluatorCache(eOpenSubdivEvaluator evaluator_type);
+
+void openSubdiv_deleteEvaluatorCache(OpenSubdiv_EvaluatorCache *evaluator_cache);
+
+// Return the GLSL source code from the OpenSubDiv library used for patch evaluation.
+// This function is not thread-safe.
+const char *openSubdiv_getGLSLPatchBasisSource(void);
+
#ifdef __cplusplus
}
#endif