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:
authorKévin Dietrich <kevin.dietrich@mailoo.org>2021-12-27 18:34:47 +0300
committerKévin Dietrich <kevin.dietrich@mailoo.org>2021-12-27 18:35:54 +0300
commiteed45d2a239a2a18a2420ba15dfb55e0f8dc5630 (patch)
treeaa55ce966caa8e28db4853d7d755003ed249805b /intern/opensubdiv
parent31e120ef4997583332aa9b5af93521e7e666e9f3 (diff)
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for faster renders of meshes with a subdivision surface modifier placed at the last position in the modifier list. When evaluating the subsurf modifier, we detect whether we can delegate evaluation to the draw code. If so, the subdivision is first evaluated on the GPU using our own custom evaluator (only the coarse data needs to be initially sent to the GPU), then, buffers for the final `MeshBufferCache` are filled on the GPU using a set of compute shaders. However, some buffers are still filled on the CPU side, if doing so on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose logic is hardly GPU compatible). This is done at the mesh buffer extraction level so that the result can be readily used in the various OpenGL engines, without having to write custom geometry or tesselation shaders. We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in order to control the data layout, and interpolation. For example, we store vertex colors as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float types. In order to still access the modified geometry on the CPU side, for use in modifiers or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`. Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will create such a wrapper if possible. If the final subdivision surface is not needed on the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used. Enabling or disabling GPU subdivision can be done through the user preferences (under Viewport -> Subdivision). See patch description for benchmarks. Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport Differential Revision: https://developer.blender.org/D12406
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